《剑指offer》面试题答案汇总(Java版)

38 篇文章 2 订阅
6 篇文章 0 订阅
面试题2:实现Singleton模式
(1)饿汉模式
public class Singleton{
    private static Singleton instance = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return instance;
    }
}

需要使用static来保证是类层面的

(2)懒汉模式
public class Singleton{
    private static Singleton instance = new Singleton();
    private Singleton(){}
    public static synchronized Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

(3)双重锁

public class Singleton{
    private static Singleton instance = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        if(instance == null){
            synchronized(Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

(4)静态内部类
public class Singleton{
    private static class SingletonHandler{
        public static Singleton newInstance = new Singleton();
    }
    private Singleton(){}
    public static Singleton getInstance(){
        return SingletonHandler.newInstance;
    }
}

面试题3:二维数组中的查找
数组的每一行按照从左到右递增,从上到下递增排列,给定一个数和数组,判断这个数是否在数组中出现。
时间复杂度:O(max(m,n)),空间复杂度:O(1)
private static boolean isIn(int[][] a,int num){
    int m = a.length;
    int n = a[0].length;
    int i = 0;
    while(i < m & n > 0){
        if(a[i][n-1] == num){
            return true;
        }else if(a[i][n-1] > num){
            n--;
        }else{
            i++;
        }
    }    
    return false;
}

面试题4:替换空格

时间复杂度:O(n),空间复杂度

public static void main(String[] args) {
    char[] arr = {'w','e',' ','a','r','e',' ','h','a','p','p','y'};
    String str = new String(arr);
    System.out.println(""+replace2(str));
    System.out.println(""+replace3(arr));
}
public static String replace3(char[] arr){
    if(arr == null || arr.length == 0){
        return "";
    }
    int len = arr.length;
    int count = 0;
    for(int i = 0; i < len; i++){
        if(arr[i] == ' ')
            count++;
    }
    int nlen = len + count*2;
    char[] res = new char[nlen];
    int p1 = len - 1;
    int p2 = nlen - 1;
    while(p1 >= 0 && p2 >= 0){
        if(arr[p1] == ' '){
            res[p2--] = '0';
            res[p2--] = '2';
            res[p2--] = '%';
        }else{
            res[p2--] = arr[p1];
        }
        p1--;
    }
return new String(res);
}
public static String replace2(String str){
if(str == null || str.length() == 0){
return "";
}
int len = str.length();
StringBuilder res = new StringBuilder();
for(int i = 0; i < len; i++){
if(str.charAt(i) == ' '){
res.append("%20");
}else{
res.append(str.charAt(i));
}
}
return res.toString();
}/*当使用split的时候要考虑可能会出现几个空格相连的情况,还有全是空格的情况,这时候用split其实就不可取了*/

面试题5:从尾到头打印链表
时间复杂度:O(n),空间复杂度:O(n)
 public static void printList(ListNode head){
        if(head == null)
            return;
        Stack<ListNode> stack = new Stack<ListNode>();
        while(head != null){
            stack.push(head);
            head = head.next;
        }
        while(!stack.isEmpty()){
            System.out.print(stack.pop().val+" ");
        }
    }/*当链表很长的时候用递归可能会栈溢出,所以还是使用栈的方法鲁棒性更好*/
    public static void printList2(ListNode head){
        if(head == null)
            return;
        if(head.next != null)
            printList2(head.next);
        System.out.print(head.val+" ");
    }

面试题6:重建二叉树(树的遍历)
时间复杂度:O(n)?,空间复杂度:O(1)
  public static TreeNode reBuild(int[] preorder, int[] inorder){
        if(preorder == null || inorder == null || inorder.length <= 0 || inorder.length != preorder.length){
            return null;
        }
        int len = inorder.length;
        return constructTree(preorder,inorder,0,len-1,0,len-1);
    }
    public static TreeNode constructTree(int[] preorder, int[] inorder, int pstart, int pend, int istart, int iend){
        if(pstart > pend || istart > iend)
            return null;
        int rootVal = preorder[pstart];
        TreeNode root = new TreeNode(rootVal);
        int index = -1;
        for(int i = istart; i <= iend; i++){
            if(inorder[i] == rootVal){
                index = i;
                break;
            }
        }
        int cur = index - istart;
        TreeNode left = constructTree(preorder, inorder, pstart+1, pstart + cur, istart, index - 1);
        TreeNode right = constructTree(preorder, inorder, pstart + cur + 1, pend, index + 1, iend);
        root.left = left;
        root.right = right;
        return root;
    }

面试题7:用两个栈实现队列
public class stackToQueue7 {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    public int pop(){
        int res = -1;
        if(!stack2.isEmpty()){
            res = stack2.pop();
        }else{
            while(!stack1.isEmpty()){
                int tmp = stack1.pop();
                stack2.push(tmp);
            }
            if(!stack2.isEmpty()){
                res = stack2.pop();
            }
        }
        return res;
    }
    public void push(int num){
        stack1.push(num);
    }
}

用两个队列实现一个栈:
public class queueToStack7 {
    Queue<Integer> queue1 = new LinkedList<Integer>();
    Queue<Integer> queue2 = new LinkedList<Integer>();
    public int pop() throws Exception{
        if(queue1.isEmpty() && queue2.isEmpty()){
            throw new Exception("stack is empty");
        }
        if(queue1.isEmpty()){
            while(queue2.size() > 1){
                queue1.add(queue2.poll());
            }
            return queue2.poll();
        }
        if(queue2.isEmpty()){
            while(queue1.size() > 1){
                queue2.add(queue1.poll());
            }
            return queue1.poll();
        }
        return (Integer) null;
    }
    public void push(int num){
        if(queue1.isEmpty() && queue2.isEmpty()){
            queue1.add(num);
            return;
        }
        if(!queue1.isEmpty()){
            queue1.add(num);
            return;
        }
        if(!queue2.isEmpty()){
            queue2.add(num);
            return;
        }
    }
}

面试题8:旋转数组的最小数字(二分查找)
注意要考虑到数组是全部顺序以及10111顺序的数组等特殊情况。
时间复杂度O(lgn)
注:二分查找适合于排序或者部分排序数组的处理
public static int minInSpiral(int[] arr) throws Exception{
        if(arr == null || arr.length == 0){
            throw new Exception("Error");
        }
        int p1 = 0;
        int p2 = arr.length - 1;
        int mid = p1;
        while(arr[p1] >= arr[p2]){
            if(p2 - p1 == 1){
                mid = p2;
                break;
            }
            mid = (p1 + p2) / 2;
            if(arr[p1] == arr[p2] && arr[p1] == arr[mid]){
                return getMinorder(arr,p1,p2);
            }
            if(arr[p1] <= arr[mid]){
                p1 = mid;
            }else if(arr[p2] >= arr[mid]){
                p2 = mid;
            }
        }
        return arr[mid];
    }
    public static int getMinorder(int[] arr,int start, int end){
        int tmp = arr[start];
        for(int i = start+1; i <= end; i++){
            if(arr[i] < tmp)
                tmp = arr[i];
        }
        return tmp;
    }

面试题9:斐波那契数列

   private static long[] frr = new long[10000];
    public static long fabon(int n){
        if(n == 0)
            return 0;
        if(n == 1)
            return 1;
        if(frr[n] != 0)
            return frr[n];
        frr[n] = fabon(n-1) + fabon(n-2);
        return frr[n];
    }/*递归的方法,但是效率和空间利用率都很低,且容易栈溢出*/
    public static int Fibonacci(int n) {
        if(n == 0)
            return 0;
        if(n == 1)
            return 1;
        int a1 = 1;
        int a2 = 1;
        for(int i=3;i<=n;i++){
            int tmp = a2;
            a2 = a1 + a2;
            a1 = tmp;
        }
        return a2;
    }/*时间复杂度是O(n),但是很使用*/

拓展:青蛙跳台阶(一次可以跳1阶或者2阶,f(n) = f(n-1)+f(n-2))就是菲波那切数列;
如果改成一次可以跳任意阶,则可以用数学归纳法得到f(n) = 2^n。
用2*1的矩形去覆盖大矩形也是菲波那切数列。
面试题10:二进制中1的个数
public int NumberOf1(int n) {
        int res = 0;
        while( n != 0){
            res++;
            n = n & (n -1);
        }
        return res;
    }

相似的问题还有:判断一个数是否是2的整数次方(就用n&(n-1));

判断两个整数m和n,其中一个需要变化多少位才能变成另一个数,先异或两个数,然后求异或结果中有多少个1。
面试题11:数值的整数次方
注意double是不精确的值,不能用==判断与0的关系
public static double getPow(double base, int exp) throws Exception{
        if(equal(base,0.0) && exp < 0)
            throw new Exception("0 error");
        if(exp == 0)
            return 1.0;
        int tmp = 0;
        if(exp < 0){
            tmp = exp;
            exp = - exp;
        }
        double res = getPowtmp(base,exp);
        if(tmp != 0){
            res = 1.0 / res;
        }
        return res;
    }
    public static double getPowtmp(double base, int exp){
        if(exp == 0)
            return 1.0;
        if(exp == 1)
            return base;
        double result = getPowtmp(base,exp >> 1);
        result *= result;
        if((exp & 0x1) == 1){
            result *= base;
        }
        return result;
    }
    public static boolean equal(double d1, double d2){
        if(d1 - d2 > -0.0000001 && d1 - d2 < 0.0000001){
            return true;
        }
        return false;
    }

面试题12:打印1到最大的n位数
public static void printN(int n){
        if(n <= 0)
            return;
        char[] number = new char[n+1];
        for(int i = 0; i < n+1; i++){
            number[i] = '0';
        }
        while(!increment(number)){
            sprint(number);
        }
    }
    public static void sprint(char[] arr){
        int len = arr.length;
        boolean isStart = false;
        for(int i = 0; i < len; i++){
            if(!isStart && arr[i] != '0')
                isStart = true;
            if(isStart){
                System.out.print(arr[i]);
            }
        }

    }
    public static boolean increment(char[] arr){
        boolean res = false;
        int cre = 1;
        int len = arr.length;
        for(int i = len - 1; i >= 0; i--){
            int sum = arr[i] - '0' + cre;
            if(sum >= 10){
                if(i == 0)
                    res = true;
                else{
                    sum -= 10;
                    cre = 1;
                    arr[i] = (char) ('0' + sum);
                }
            }else{
                arr[i] = (char)('0' + sum);
                break;
            }
        }
        return res;
    }

上面是常规解法,实际上还可以把此题看作是n个从0到9的全排列问题,只是在前面的0不打印。
public static void printN2(int n){
        if(n <= 0)
            return;
        char[] number = new char[n+1];
        for(int i = 0; i < n+1; i++){
            number[i] = '0';
        }
        for(int i = 0; i < 10; i++){
            number[0] = (char)(i + '0');
            printRe(number,n,0);
        }
    }
    public static void printRe(char[] arr,int len, int index){
        if(index == len - 1){
            sprint(arr);
            return;
        }
        for(int i = 0; i < 10; i++){
            arr[index+1] = (char)(i + '0');
            printRe(arr,len,index+1);
        }
    }
面试题13:在O(1)时间删除链表节点
将待删除结点的下一个结点的值赋给待删除的结点,然后删除一个结点,就达到了删除结点的目的。
找到合适的 算法之后,还有几种情况需要考虑:
1)正常, 多个结点,删除的不是尾结点。
2)只有一个结点,删除尾结点(也是头结点)
3)有多个结点,删除尾结点
(注意一种特殊情况就是要删除的点不在链表中)
public static void deleteOne(ListNode head,ListNode del){
        if(head == null || del == null)
            return;
        if(del.next != null){
            del.val = del.next.val;
            del.next = del.next.next;
        }else if(del == head){
            head = null;
        }else{
            ListNode tmp = head;
            while(tmp.next != del)
                tmp = tmp.next;
            tmp.next = null;
        }
    }

面试题14:调整数组顺序使奇数位于偶数前面
public static void changeOrder(int[] arr){
        if(arr == null)
            return;
        int len = arr.length;
        int start = 0;
        int end = len - 1;
        while(start < end){
            while(start < end && !isEven(arr[start])){
                start++;
            }
            while(start < end && isEven(arr[end]))
                end--;
            if(start < end){
                int tmp = arr[start];
                arr[start] = arr[end];
                arr[end] = tmp;
            }
        }
    }
    public static boolean isEven(int n){
        if((n & 1) == 0){
            return true;
        }
        return false;
    }

面试题15:链表中的倒数第K个结点
public static ListNode getKth(ListNode head,int k){
        if(head == null || k <= 0)
            return null;
        ListNode pre = head;
        for(int i = 0; i < k; i++){
            pre = pre.next;
            if(pre == null)
                return pre;
        }
        ListNode cur = head;
        while(pre != null){
            pre = pre.next;
            cur = cur.next;
        }
        return cur;
    }

面试题16:反转链表
public static ListNode reverse(ListNode head){
        ListNode pre = null;
        ListNode cur = head;
        ListNode tmp = null;
        while(cur != null){
            tmp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }

面试题17:合并两个排序的链表
public static ListNode mergeTwo(ListNode list1,ListNode list2){
        if(list1 == null)
            return list2;
        if(list2 == null)
            return list1;
        ListNode nHead = null;
        if(list1.val >= list2.val){
            nHead = list1;
            list1 = list1.next;
        }else{
            nHead = list2;
            list2 = list2.next;
        }
        ListNode p = nHead;
        while(list1 != null && list2 != null){
            if(list1.val >= list2.val){
                p.next = list1;
                list1 = list1.next;
            }else{
                p.next = list2;
                list2 = list2.next;
            }
            p = p.next;
        }
        p.next = list1 == null ? list2 : list1;
        return nHead;
    }

面试题18:树的子结构(树的遍历)
public static boolean isSubtr(TreeNode pRoot,TreeNode sRoot){
        if(pRoot == null || sRoot == null)
            return false;
        return isSubImpl(pRoot,sRoot);
    }
    public static boolean isSubImpl(TreeNode pRoot,TreeNode sRoot){
        if(sRoot == null)
            return true;
        if(pRoot == null)
            return false;
        boolean res = false;
        if(pRoot.val == sRoot.val){
            res = isSubImpl(pRoot.left,sRoot.left) && isSubImpl(pRoot.right,sRoot.right);
        }
        if(!res){
            res = isSubImpl(pRoot.left,sRoot) || isSubImpl(pRoot.right,sRoot);
        }
        return res;
    }

面试题19:二叉树的镜像
public static void mirror(TreeNode root){
        if(root == null || (root.left == null && root.right == null))
            return ;
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
        if(root.left != null){
            mirror(root.left);
        }
        if(root.right != null)
            mirror(root.right);
    }

面试题20:顺时针打印矩阵
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printMatrix(int [][] matrix) {
       if(matrix == null || matrix.length ==0 || matrix[0].length == 0)
           return null;
        int rows = matrix.length;
        int cols = matrix[0].length;
        int start = 0;
        ArrayList<Integer> res = new ArrayList<>();
        while(cols > start * 2 && rows > start * 2){
            printMatrixInCircle(res, matrix, rows, cols, start);
            start++;
        }
        return res;
    }
    private void printMatrixInCircle(ArrayList<Integer> res, int[][] arr, int row, int col,int start){
        int endX = col - 1 - start;
        int endY = row - 1 - start;
        //从左到右打印
        for(int i = start; i <= endX; i++){
            res.add(arr[start][i]);
        }
        //从上到下打印最右边一列
        if(endY > start){
            for(int i = start + 1; i <= endY; i++){
                res.add(arr[i][endX]);
            }
        }
        //从右到左打印最后一行
        if(endX > start && endY > start){
            for(int i = endX - 1; i >= start; i--){
                res.add(arr[endY][i]);
            }
        }
        //从下到上打印第一列
        if(endX > start && endY > start + 1){
            for(int i = endY - 1; i > start; i--){
                res.add(arr[i][start]);
            }
        }
    }
}
public static void printCircle(int[][] matrix) {
        if (matrix == null)
            return;
        int rows = matrix.length;
        int cols = matrix[0].length;
        int left = 0;
        int top = 0;
        int right = cols - 1;
        int bottom = rows - 1;
        while (top <= bottom && left <= right) {
            for (int i = left; i <= right; i++) {
                System.out.print(matrix[top][i] + " ");
            }
            top++;
            if (top > bottom)
                break;
            for (int j = top; j <= bottom; j++) {
                System.out.print(matrix[j][right] + " ");
            }
            right--;
            if (right < left)
                break;
            for (int i = right; i >= left; i--) {
                System.out.print(matrix[bottom][i] + " ");
            }
            bottom--;
            for (int j = bottom; j >= top; j--) {
                System.out.print(matrix[j][left] + " ");
            }
            left++;
        }
    }

面试题21:包含min函数的栈
注:这种方法还有可以优化的地方,因为min栈中可能会有很多重复的元素;
public class hasMinStack21 {
Stack<Integer> dataStack = new Stack<Integer>();
Stack<Integer> minStack = new Stack<Integer>();
public void pop() throws Exception{
if(dataStack.size() == 0 || minStack.size() == 0)
throw new Exception("stack is null");
dataStack.pop();
minStack.pop();
}
public void push(int num){
dataStack.push(num);
if(minStack.size() == 0 || minStack.peek() > num)
minStack.push(num);
else
minStack.push(minStack.peek());
}
public int getMin() throws Exception{
if(minStack.size() == 0)
throw new Exception("stack is null");
return minStack.peek();
}
}
面试题22:栈的压入、弹出序列(栈)
public static boolean isPop(int[] push,int[] pop){
if(push == null || pop == null || push.length != pop.length)
return false;
Stack<Integer> tmp = new Stack<Integer>();
int len = push.length;
int index = 0;
for(int i = 0; i < len; i++){
while(tmp.isEmpty() || tmp.peek() != pop[i]){
if(index >= len)
return false;
tmp.push(push[index++]);
}
tmp.pop();
}
return true;
}
面试题23:从上到下遍历二叉树(宽度优先遍历,队列)
public static void printTreeNode(TreeNode root){
        if(root == null)
            return;
        Stack<TreeNode> stack = new Stack<TreeNode>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode cur = stack.pop();
            System.out.print(cur.val + " ");
            if(cur.left != null)
                stack.push(root.left);
            if(cur.right != null)
                stack.push(root.right);
        }
    }

面试题24:二叉树的后续遍历序列(树的遍历)
 public static boolean isPost(int[] arr,int start, int end){
        if(arr == null || arr.length == 0)
            return false;
        int root = arr[end];
        int i = start;
        for(; i < end; i++){
            if(arr[i] > root)
                break;
        }
        for(int j = i; j < end; j++){
            if(arr[j] < root)
                return false;
        }
        boolean left = true;
        if(i > start)
            left = isPost(arr,start,i-1);
        boolean right = true;
        if(i < end)
            right = isPost(arr,i,end-1);
        return left & right;
    }

面试题25:二叉树中何为某一值得路径
这种方式的空间复杂度要低于Map,时间复杂度仍然为O(n)。
public static ArrayList<ArrayList<Integer>> getPath(TreeNode root,int sum){
        ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
        if(root == null)
            return res;
        ArrayList<Integer> li = new ArrayList<Integer>();
        getPathImpl(root,sum,res,li);
        return res;
    }
    public static void getPathImpl(TreeNode root, int sum, ArrayList<ArrayList<Integer>> res, ArrayList<Integer> li){
        if(root == null)
            return;
        li.add(root.val);
        sum -= root.val;
        if(root.left == null && root.right == null){
            if(sum == 0){
                res.add(new ArrayList<Integer>(li));
            }
        }else{
            if(root.left != null)
                getPathImpl(root.left,sum,res,li);
            if(root.right != null)
                getPathImpl(root.right,sum,res,li);
        }
        li.remove(li.size() - 1);
    }

面试题26:复杂链表的复制(带next的链表复制)
旧方法:
 public RandomListNode Clone(RandomListNode pHead)
    {
        if(pHead == null)
            return null;
        Map<RandomListNode,RandomListNode> map = new HashMap<RandomListNode,RandomListNode>();
        RandomListNode newhead = new RandomListNode(pHead.label);
        map.put(pHead,newhead);
        RandomListNode p = pHead.next;
        RandomListNode np = newhead;
        while(p != null){
            RandomListNode tmp = new RandomListNode(p.label);
            map.put(p,tmp);
            np.next = tmp;
            p = p.next;
            np = np.next;
        }
        np = newhead;
        p = pHead;
        while(np != null){
            np.random = map.get(p.random);
            np = np.next;
            p = p.next;
        }
        return newhead;
    }
以下方法的时间复杂度仍然是O(n),但空间复杂度比上面的好。
public static ComplexListNode copyList(ComplexListNode head){
if(head == null)
return head;
copyNode(head);
setSibling(head);
return getNewHead(head);
}
public static void copyNode(ComplexListNode head){
while(head != null){
ComplexListNode clone = new ComplexListNode(head.val);
clone.next = head.next;
head.next = clone;
head = clone.next;
}
}
public static void setSibling(ComplexListNode head){
while(head != null){
head.next.sibling = head.sibling.next;
head = head.next.next;
}
}
public static ComplexListNode getNewHead(ComplexListNode head){
ComplexListNode phead = head.next;
ComplexListNode cur = phead;
head = cur.next;
while(head != null){
cur.next = head.next;
cur = cur.next;
head = cur.next;
}
return phead;
}
面试题27:二叉搜索树与双向链表(双向链表,二叉搜索树)
public static TreeNode treeToList(TreeNode root){
if(root == null)
return root;
TreeNode last = convertList(root,null);
while(last.left != null)
last = last.left;
return last;
}
public static TreeNode convertList(TreeNode root,TreeNode pre){
if(root == null)
return null;
if(root.left != null){
pre = convertList(root.left,pre);
}
root.left = pre;
if(pre != null)
pre.right = root;
pre = root;
if(root.right != null)
pre = convertList(root.right,pre);
return pre;
}
面试题28:字符串的全排列
以下代码只适用于字符串中没有重复字符的情况。
public static ArrayList<String> permutation(String str){
ArrayList<String> res = new ArrayList<String>();
if(str == null || str.length() == 0)
return res;
char[] arr = str.toCharArray();
parseStr(arr,res,0);
return res;
}
public static void parseStr(char[] str,ArrayList<String> res,int start){
int len = str.length - 1;
if(start == len){
res.add(String.valueOf(str));
}else{
for(int i = start; i <= len; i++){
char tmp = str[i];
str[i] = str[start];
str[start] = tmp;
parseStr(str,res,start+1);
tmp = str[i];
str[i] = str[start];
str[start] = tmp;
}
}
}
与排列对应的还有组合问题,注意ab和ba算一个组合:
public static ArrayList<String> combination(String str){
ArrayList<String> res = new ArrayList<String>();
if(str == null || str.length() == 0){
return res;
}
int len = str.length();
ArrayList<Character> li = new ArrayList<Character>();
for(int i = 1; i <= len; i++){
combin(str,0,i,res,li);
}
return res;
}
public static void combin(String str, int begin, int number, ArrayList<String> res,ArrayList<Character> li){
if(number == 0){
res.add(li.toString());
return;
}
if(begin == str.length())
return;
li.add(str.charAt(begin));
combin(str,begin+1,number-1,res,li);
li.remove((Character)str.charAt(begin));
combin(str,begin+1,number,res,li);
}
拓展:八皇后问题
(1)金典上给的解法是一个个的放皇后,将可以放得位置存到col[i]=col,每次判断是否在同一列,或者对角线上;
(2)剑指offer给的解法是,用col[i]=m表示第i行的皇后的列号是m,然后初始化这个数组为0到7的数字,然后对数组做全排列,最后只需判断8个皇后是不是在同一对角线上,判断方法是i-j == col[i] - col[j] 或者 j-i == col[i] - col[j]。
面试题29:数组中出现次数超过一半的数字
public static int getMoreHalf(int[] nums){
if(nums == null || nums.length == 0)
return -1;
int count = 1;
int cur = nums[0];
int len = nums.length;
for(int i = 1; i < len; i++){
if(count == 0){
cur = nums[i];
count = 1;
}else if(nums[i] == cur)
count++;
else{
count--;
}
}
count = 0;
for(int i = 0; i < len; i++){
if(nums[i] == cur)
count++;
}
if(count > len / 2)
return cur;
return -1;
}
面试题30:求最小的k个数字
基于快速排序思想做,时间复杂度是O(n)。
当需要处理的是海量数据的时候,考虑用大根堆或者小根堆比较实用,时间复杂度是O(nlogn)。这种方法不会改变原数组,上面那种需要改变原数组。
public static void getMinestK(int[] arr, int[] out, int k){
if(arr == null || out == null || k <= 0 || arr.length < k || out.length < k)
return;
int len = arr.length;
int start = 0;
int end = len - 1;
int index = quick(arr,start,end);
while(index != (k-1)){
if(index < k - 1){
start = index + 1;
index = quick(arr,start,end);
}else{
end = index - 1;
index = quick(arr,start,end);
}
}
for(int i = 0; i < k; i++){
out[i] = arr[i];
}
}
public static int quick(int[] arr,int start, int end){
int key = arr[start];
while(start < end){
while(start < end && arr[end] >= key)
end--;
arr[start] = arr[end];
while(start < end && arr[start] <= key)
start++;
arr[end] = arr[start];
}
arr[start] = key;
return start;
}
面试题31:连续子数组的最大和
public static int getMaxSum(int[] arr){
if(arr == null || arr.length == 0)
return 0;
int len = arr.length;
int sum = arr[0];
int max = arr[0];
for(int i = 1; i < len; i++){
if(sum <= 0)
sum = arr[i];
else
sum += arr[i];
max = sum > max ? sum : max;
}
return max;
}
面试题32:从1到n整数中1出现的次数
时间复杂度是O(logn)
public static int getTimes1(int n){
if(n <= 0)
return 0;
int count = 0;
int base = 1;
int round = n;
while(round > 0){
int weight = round % 10;
round /= 10;
count += round*base;
if(weight > 1){
count += base;
}else if(weight == 1){
count += (n % base) + 1;
}
base *= 10;
}
return count;
}
面试题33:把数组排成最小的树
使用快排+字符串比较
public static String getMinNum(int[] arr){
String res = "";
if(arr == null || arr.length == 0)
return res;
int len = arr.length;
myQuickSort2(arr,0,len-1);
for(int i = 0; i < len; i++){
res += arr[i];
}
return res;
}
public static void myQuickSort(int[] arr,int start,int end){
if(start < end){
int mid = sortCore(arr,start,end);
myQuickSort(arr,start,mid-1);
myQuickSort(arr,mid+1,end);
}
}
public static int sortCore(int[] arr,int low, int high){
int key = arr[low];
while(low < high){
while(low < high && !isSmall(arr[high],key)){
high--;
}
arr[low] = arr[high];
while(low < high && !isSmall(key,arr[low]))
low++;
arr[high] = arr[low];
}
arr[low] = key;
return low;
}
public static boolean isSmall(int a,int b){
String s1 = "" + a + b;
String s2 = "" + b + a;
if(s1.compareTo(s2) == -1){
return true;
}
return false;
}
public static void myQuickSort2(int[] arr,int left,int right){
if(left < right){
int main_number = arr[right];
int small_cur = left;
for(int j = left;j<right;j++){
if(isSmall(arr[j],main_number)){
int temp = arr[j];
arr[j] = arr[small_cur];
arr[small_cur] = temp;
small_cur++;
}
}
arr[right]= arr[small_cur];
arr[small_cur] = main_number;
myQuickSort2(arr,left,small_cur-1);
myQuickSort2(arr,small_cur+1,right);
}
}
面试题34:丑数
public static int getNthUglynumber(int n){
if(n <= 0)
return 0;
int[] uglynum = new int[n];
uglynum[0] = 1;
int t2 = 1;
int t3 = 1;
int t5 = 1;
int index = 1;
while(index < n){
int cur = minNum(t2*2,t3*3,t5*5);
uglynum[index] = cur;
while(t2 * 2 <= cur)
t2++;
while(t3 * 3 <= cur)
t3++;
while(t5 * 5 <= cur)
t5++;
index++;
}
return uglynum[n-1];
}
public static int minNum(int num1,int num2,int num3){
int min = num1 > num2 ? num2 : num1;
min = min > num3 ? num3 : min;
return min;
}
面试题35:第一个只出现一次的字符(哈希表)
这种方式比使用hashmap省空间
public static char getFirstChar(String str){
if(str == null)
return '\0';
char[] arr = str.toCharArray();
int len = str.length();
int[] hash = new int[256];
for(int i = 0; i < len; i++){
hash[arr[i]]++;
}
for(int i = 0; i < len; i++){
if(hash[arr[i]] == 1)
return arr[i];
}
return '\0';
}
拓展:从第一个字符串中删除在第二个字符串中出现过的所有字符:
将第二个字符串用上面的hash数组遍历和标记一下,然后遍历第一个字符串,数组中值不为0(找到)则删除。
面试题36:数组中的逆序对
public class Solution {
    public int InversePairs(int [] array) {
        if(array == null || array.length == 0){
            return 0;
        }
        return getInversePairs(array, 0, array.length - 1);
    }
    private int getInversePairs(int[] arr, int start,int end){
        if(start == end)
            return 0;
        int mid = (start + end) >> 1;
        int left = getInversePairs(arr, start, mid) % 1000000007;
        int right = getInversePairs(arr, mid + 1, end) % 1000000007;
        
        int lend = mid;
        int rend = end;
        int copyLen = end - start + 1;
        int[] copy = new int[copyLen];
        //逆序对
        int count = 0;
        int copyIndex = copyLen - 1;
        while(lend >= start && rend >= mid + 1){
            if(arr[lend] > arr[rend]){
                count += rend - mid;
                if(count >= 1000000007){
                    count %= 1000000007;
                }
                copy[copyIndex] = arr[lend];
                lend--;
            }else{
                copy[copyIndex] = arr[rend];
                rend--;
            }
            copyIndex--;
        }
        //处理两个数组剩下的部分
        while(lend >= start){
            copy[copyIndex--] = arr[lend--];
        }
        while(rend >= mid + 1){
            copy[copyIndex--] = arr[rend--];
        }
        //复制回原来的数组
        for(int i = 0; i < copyLen; i++){
            arr[i + start] = copy[i];
        }
        return (count + left + right) % 1000000007;
    }
}

面试题37:两个链表的第一个公共结点
public static ListNode getTheFirst(ListNode l1,ListNode l2){
if(l1 == null || l2 == null)
return null;
int len1 = getLength(l1);
int len2 = getLength(l2);
if(len1 < len2){
ListNode tmp = l1;
l1 = l2;
l2 = tmp;
}
int differ = len1 - len2;
while(differ > 0){
l1 = l1.next;
differ--;
}
while(l1 != null && l2 != null && l1 != l2){
l1 = l1.next;
l2 = l2.next;
}
return l1;
}
public static int getLength(ListNode head){
int res = 0;
while(head != null){
res++;
head = head.next;
}
return res;
}
面试题38:数字在排序数组中出现的次数(二分查找)
二分查找的时间复杂度是O(lgn)
public static int getNumOfK(int[] arr,int k){
if(arr == null || arr.length == 0 || k < arr[0])
return 0;
int count = 0;
int first = getFirstK(arr,k,0,arr.length-1);
if(first == -1)
return count;
int last = getLastK(arr,k,0,arr.length-1);
count = last - first + 1;
return count;
}
public static int getFirstK(int[] arr,int k,int start,int end){
if(start < end)
return -1;
int mid = (start + end) / 2;
if(arr[mid] == k){
if((mid > 0 && arr[mid-1] < k) || mid == 0){
return mid;
}else
end = mid - 1;
}else if(arr[mid] > k){
end = mid - 1;
}else
start = mid + 1;
return getFirstK(arr,k,start,end);
}
public static int getLastK(int[] arr,int k,int start,int end){
if(start < end)
return -1;
int mid = (start + end) / 2;
if(arr[mid] == k){
if((mid < end && arr[mid+1] > k) || mid == end){
return mid;
}else
start = mid + 1;
}else if(arr[mid] > k){
end = mid - 1;
}else
start = mid + 1;
return getLastK(arr,k,start,end);
}
面试题39:二叉树的深度(树的遍历)以及判断二叉树是否为平衡二叉树
public static int theDepth(TreeNode root){
if(root == null)
return 0;
int left = theDepth(root.left);
int right = theDepth(root.right);
return left > right ? left + 1 : right + 1;
}
public static boolean isBalance(TreeNode root){
if(depthAndBalance(root) == -1)
return false;
return true;
}
//后序遍历的思想
public static int depthAndBalance(TreeNode root){
if(root == null){
return 0;
}
int left = depthAndBalance(root.left);
if(left == -1)
return -1;
int right = depthAndBalance(root.right);
if(right == -1)
return -1;
if(right - left > 1 || right - left < -1)
return -1;
return right > left ? right + 1 : left + 1;
}
面试题40:数组中只出现一次的两个数字(二进制)
public static void getTheTwo(int[] arr,int num1,int num2){
if(arr == null || arr.length <= 1)
return;
int len = arr.length;
int tmp = arr[0];
for(int i = 1; i < len; i++){
tmp ^= arr[i];
}
int index = findFirstOne(tmp);
for(int i = 0; i < len; i++){
if(isOne(arr[i],index)){
num1 ^= arr[i];
}else{
num2 ^= arr[i];
}
}
}
public static int findFirstOne(int n){
int i = 0;
while((n & 1) == 0){
i++;
n = n >> 1;
}
return i;
}
public static boolean isOne(int num,int index){
if(((num >> index) & 1) == 1)
return true;
return false;
}
面试题41:和为S的两个数字,何为S的连续正数序列
public static void getTwoNumEqualS(int[] arr,int s,int num1,int num2){
if(arr == null || arr.length < 2 || s < arr[0])
return;
int len = arr.length;
int start = 0;
int end = len - 1;
while(start < end){
int sum = arr[start] + arr[end];
if(sum == s){
num1 = arr[start];
num2 = arr[end];
return;
}else if(sum > s){
end--;
}else
start++;
}
}
public static ArrayList<ArrayList<Integer>> getSumEqualS(int s){
ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
if(s < 3)
return res;
int small = 1;
int big = 2;
int cur = small + big;
int mid = (s+1)/2;
while(small < mid){
if(cur == s){
ArrayList<Integer> li = new ArrayList<Integer>();
for(int i = small; i <= big; i++){
li.add(i);
}
res.add(li);
}
while(cur > s && small < mid){
cur -= small;
small++;
}
big++;
cur += big;
}
return res;
}
面试题42:反转单词顺序,左旋字符串
我的第一想法是先将字符串拆分为单词,然后再以单词为单位反转,但是这种方式的时间复杂度是O(n),空间复杂度也是O(n);
剑指offer上的方法是先反转整个句子,再反转每个单词,这种方法的时间复杂度也是O(n),空间复杂度是O(1)。
 public static String reverseStr(String s){
        if(s == null || s.length() == 0){
            return "";
        }
        int len = s.length();
        char[] arr = s.toCharArray();
        reverse(arr,0,len-1);
        int count = 0;
        for(int i = 0; i < len; i++){
            if(arr[i] != ' ')
                count++;
            else{
                if(count > 1){
                    reverse(arr,i - count,i-1);
                }
                count = 0;
            }
        }
        return String.valueOf(arr);
    }
    public static void reverse(char[] arr,int start,int end){
        while(start < end){
            char tmp = arr[start];
            arr[start] = arr[end];
            arr[end] = tmp;
            start++;
            end--;
        }
    }
public class Solution {
    public String ReverseSentence(String str) {
        if(str == null || str.length() < 2)
            return str;
        String res = "";
        String[] arr = str.split(" ");
        int len = arr.length;
        if(len > 0){ 
            for(int i = len - 1; i > 0; i--){
                res += arr[i] + " ";
            }
            res += arr[0];
        }else{
            res = str;
        }
        return res;
    }
}

对于左旋字符串,如果要求空间复杂度为O(1)的话也可以采用上面的方法,先将前一部分reverse,后一部分reverse,然后再整个reverse:
public static String getReverseStr1(String s,int n){
//其他略
char[] arr = s.toCharArray();
int len = s.length();
reverse(arr,0,n-1);
reverse(arr,n,len-1);
reverse(arr,0,len-1);
return String.valueOf(arr);
}
如果没有空间复杂度的限制,可以将前后两部分分别放在一个String,然后return s2+s1就可以。
public static String getReverseStr2(String s,int n){
//其他略
int len = s.length();
StringBuilder s1 = new StringBuilder();
StringBuilder s2 = new StringBuilder();
for(int i = 0; i < len; i++){
if(i < n)
s1.append(s.charAt(i));
else
s2.append(s.charAt(i));
}
return s2.toString() + s1.toString();
}
面试题43:n个骰子的点数
public static void getDice(int n,double[] res){
if(n <= 0)
return;
int MAX = 6;
int len = 6 * n;
int[][] tmpArr = new int[2][len+1];
int flag = 0;
for(int i = 1; i <= MAX; i++)
tmpArr[0][i] = 1;
for(int i = 2; i <= n; i++){
for(int k = 0; k < i; k++){
tmpArr[1-flag][k] = 0;
}
for(int k = i; k <= MAX*i; k++){
tmpArr[1-flag][k] = 0;
for(int j = 1; j <= k && j <= MAX; j++)
tmpArr[1-flag][k] += tmpArr[flag][k-j];
}
flag = 1 - flag;
}
int total = n * 5 + 1;
res = new double[total];
for(int i = 0; i< total; i++){
res[i] = (double)tmpArr[flag][n+i] / total;
}
}
面试题44:扑克牌的顺子
public static boolean isStraight(int[] nums,int len){
if(nums == null || len <= 0 || nums.length != len || len > 13){
return false;
}
sort(nums,0,len-1);//用快排,时间复杂度O(nlogn)
int numOfZero = 0;
int numOfGap = 0;
int i = 0;
while(i < len && nums[i] == 0){
numOfZero++;
i++;
}
while(i < len - 1){
if(nums[i] == nums[i+1])
return false;
numOfGap = nums[i+1] - nums[i];
i++;
}
if(numOfZero >= numOfGap)
return true;
return false;
}
面试题45:圆圈中最后剩下的数字(环形链表,约瑟夫环问题)
用环形链表的方法时间复杂度是O(mn),空间复杂度是O(n)。
时间复杂度是O(n),空间复杂度是O(1)。
public static int getLast(int n,int m){
if(n < 1 || m < 1)
return -1;
int res = 0;
for(int i = 2; i <= n; i++){
res = (res + m) % i;
}
return res;
}
面试题46:求1+2+3+。。。+n
public static int getSum(int n){
int res = n;
boolean flag = (n > 0) && ((res += getSum(n-1)) > 0);
return res;
}
面试题47:不用加减乘除做加法(二进制)
(1)先求两个数不带进位的和;
(2)求进位;
(3)将上两者加起来。
public static int and(int a, int b){
int sum = 0;
int carry = 0;
do{
sum = a ^ b;
carry = a & b << 1;
a = sum;
b = carry;
}while(b != 0);
return sum;
}:
面试题49:把字符串转换成整数
注意:考虑边界条件,输入的合法性,溢出等
private static boolean flag = true;
public static int strToInt(String str){
if(str == null || str == ""){
flag = false;
return 0;
}
int sig = 1;
int len = str.length();
char[] arr = str.toCharArray();
int i = 0;
if(arr[0] == '-'){
sig = -1;
i++;
}else if(arr[0] == '+'){
i++;
}
if(i == len){
flag = false;
return 0;
}
long res = 0;
while(i < len){
if(arr[i] >= '0' && arr[i] <= '9'){
res = res * 10 + arr[i];
if((-res < Integer.MIN_VALUE) || (res > Integer.MAX_VALUE)){
flag = false;
return 0;
}
}else{
flag = false;
return 0;
}
}
return (int) (sig * res);
}
面试题50:树中两个结点的最低公共祖先(二叉搜索树)
拓展:关于求树上两个结点的第一个公共父节点,对于二叉排序树来说就是比较大小,对于带有指向父节点指针的任意树来说就是找两个链表的第一个公共结点。
对于即不是二叉搜索树,又没有指向父节点指针的普通树,要找到第一个公共结点的方法就是从根节点遍历这棵树,先遍历根节点的一个子树,(1)如果发现两个结点都在这个子树中,则继续遍历这个子树;(2)如果发现只有一个结点在这个子树中,则此时的根节点就是第一个公共父节点;(3)如果两个几点都没有在这个子树中,则继续遍历其他子树。
注意,上面这个方法在遍历树的时候会做很多重复的对比工作,所以可以使用辅助空间,比如用两个链表保存从根节点指向两个输入结点的路径,问题就转换成求两个链表的最后的公共结点。
注:以下都是基于二叉树的,对于多叉树,只要在前序遍历的时候依次从左到右遍历子树就行。另外java中没有现成的链表,所以使用栈也能达到同样的效果。
public static TreeNode getConParent(TreeNode root,TreeNode node1,TreeNode node2){
if(root == null || node1 == null || node2 == null)
return null;
Stack<TreeNode> stack1 = null;
boolean path1 = getList(root,node1,stack1);
if(!path1)
return null;
Stack<TreeNode> stack2 = null;
boolean path2 = getList(root,node2,stack2);
if(!path2)
return null;
return findConNode(stack2,stack2);
}
public static boolean getList(TreeNode root,TreeNode p,Stack<TreeNode> list){
list.push(root);
if(root == p){
return true;
}
boolean flag = false;
if(root.left != null){
flag = getList(root.left,p,list);
}
if(!flag && root.right != null){
flag = getList(root.right,p,list);
}
if(!flag)
list.pop();
return flag;
}
public static TreeNode findConNode(Stack<TreeNode> s1,Stack<TreeNode> s2){
TreeNode res = null;
if(s1.size() > s2.size()){
while(s1.size() != s2.size())
s1.pop();
}else{
while(s1.size() != s2.size())
s2.pop();
}
res = s1.pop();
while(res != s2.pop()){
res = s1.pop();
}
return res;
}
1.数组中重复的数字(网上找的)
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。
解答:
import java.util.*;
public class Solution {
    public boolean duplicate(int numbers[],int length,int [] duplication) {
     if(numbers == null || length <= 0)
            return false;
        for(int i = 0; i < length; i++){
            if(numbers[i] < 0 || numbers[i] > length-1)
                return false;
            while(numbers[i] != i){
                if(numbers[i] == numbers[numbers[i]]){
                    duplication[0] = numbers[i];
                    return true;
                }
                int tmp = numbers[i];
                numbers[i] = numbers[tmp];
                numbers[tmp] = tmp;
            }
        }
        return false;
    }
}
1.构建乘积数组(网上找的)
给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。
解答:
将计算过程画出来,寻找规律 
如上图所示,每行跳过红色数字,黑色数字的乘积就是对应的B[i].那么以红色数字为分割线,左侧数组设为D,右侧设为C,则B= C*D; 
则 
D[0] = 1; D[i] = D[i-1] * A[i-1]; 从上到下计算 
C[len-1] = 1; C[i-1] = C[i] * A[i]; 从下到上计算 
最后的 B[i] = D[i]*C[i]; 
时间复杂度O(n),空间复杂度O(n)。
import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {
if (A == null || A.length <= 0) {  
            return null;  
        }  
        int[] c = new int[A.length];  
        int[] d = new int[A.length];  
        int[] b = new int[A.length];  
          
        d[0] = 1; // 计算D  
        for (int i = 1; i < A.length; i++) {  
            d[i] = d[i-1] * A[i-1];  
        }   
          
        c[A.length-1] = 1; // 计算C  
        for (int j = A.length-1; j > 0; j--) {  
            c[j-1] = c[j] * A[j];   
        }  
          
        // 计算B  
        for (int i = 0; i < A.length; i++) {  
            b[i] = d[i]*c[i];  
        }  
          
        return b;  
    }
}
请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配。
public static boolean isPattern(char[] s,char[] pattern){
if(s == null || pattern == null)
return false;
return patternCore(s,0,pattern,0);
}
public static boolean patternCore(char[] str,int s,char[] pattern,int p){
if(str.length <= s && pattern.length <= p)//字符串和模式都结束了
return true;
if(str.length > s && pattern.length <= p)//字符串没结束,模式结束了
return false;
if(p + 1 < pattern.length && pattern[p+1] == '*'){//模式的后一个是*
if(s >= str.length)//字符串完了
return patternCore(str,s,pattern,p+2);
else{
if(pattern[p] == str[s] || pattern[p] == '.'){
return patternCore(str,s+1,pattern,p) || patternCore(str,s+1,pattern,p+2)
|| patternCore(str,s,pattern,p+2);
}else{
patternCore(str,s,pattern,p+2);
}
}
}
//下一个模式不是*
if(s >= str.length)
return false;
else{
if(str[s] == pattern[p] || pattern[p] == '.')
return patternCore(str,s+1,pattern,p+1);
return false;
}
}
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
//参考正则匹配string.matches("[+-]?[0-9]*(\\.[0-9]*)?([eE][+-]?[0-9]+)?");
public static boolean isNumeric(char[] str){
if(str == null || str.length == 0)
return false;
int i = 0;
int len = str.length;
if(str[i] == '-' || str[i] == '+')
i++;
if(i == len)
return false;
i = scanDigits(str,i,len);
if(i < len){
if(str[i] == '.'){//有小数
i = scanDigits(str,i+1,len);
if(i < len){//有指数
return isExponextial(str,i,len);
}
}else{
return isExponextial(str,i,len);
}
}
return true;
}
public static int scanDigits(char[] str, int index,int len){
while(index < len){
if(str[index] >= '0' && str[index] <= '9')
index++;
else
return index;
}
return index;
}
public static boolean isExponextial(char[] str,int index,int len){
if(str[index] == 'e' || str[index] == 'E'){
++index;
if(str[index] == '-' || str[index] == '+'){
++index;
}
index = scanDigits(str,index,len);
if(index == len)
return true;
}
return false;
}
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
public static char findFirst(String str){
if(str == null || str.length() == 0)
return '#';
int[] hashtable = new int[256];
int len = str.length();
char[] arr = str.toCharArray();
for(int i = 0; i < len; i++){
hashtable[arr[i]]++;
}
for(int i = 0; i < len;i++){
if(hashtable[arr[i]] == 1)
return arr[i];
}
return '#';
}
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5。
public static ListNode2 delThe(ListNode2 root){
if(root == null)
return root;
ListNode2 res = null;
ListNode2 cur = null;
while(root != null && root.next != null){
if(root.val == root.next.val){
while(root.next != null && root.val == root.next.val){
root = root.next;
}
}else{
if(res == null){
res = root;
cur = res;
}else{
cur.next = root;
cur = cur.next;
}
}
root = root.next;
}
if(root != null){
cur.next = root;
}
return res;
}
1.数据流中的中位数(网上的)
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
解答:
import java.util.*;
public class Solution {
 TreeSet<Integer> max = new TreeSet<Integer>();  //大顶堆,用于存放前面一半的元素
    TreeSet<Integer> min = new TreeSet<Integer>();  //小顶堆,用于存放后面一般的元素
    /**
     * 用两个推保存数据,保持两个堆的数据保持平衡(元素个数相差不超过1)
     * 大顶堆存放的数据要比小顶堆的数据小
     * 当两个推中元素为偶数个,将新加入元素加入到大顶堆,如果要加入的数据,比小顶堆的最小元素大,
     * 先将该元素插入小顶堆,然后将小顶堆的最小元素插入到大顶堆。
     * 当两个推中元素为奇数个,将新加入元素加入到小顶堆,如果要加入的数据,比大顶堆的最大元素小,
     * 先将该元素插入大顶堆,然后将大顶堆的最大元素插入到小顶堆。
     * @param num
     */
    public void Insert(Integer num) {
        if (((max.size()+min.size()) & 1) == 0) { //偶数个
            if (min.size() > 0 && num > min.first()) {
                min.add(num);
                num = min.first();
                min.remove(num);
            }
            max.add(num);
        }else {
            if (max.size() > 0 && num < max.last()) {
                max.add(num);
                num = max.last();
                max.remove(num);
            }
            min.add(num);
        }
    }
    /**
     * 当元素个数是奇数个时,大顶堆元素比小顶堆多一个,中位数即为大顶堆的堆顶元素
     * 若为偶数,则中位数是大小顶堆的堆顶元素之和除2
     * @return
     */
    public Double GetMedian() {
        int size = max.size() + min.size();
        if (size == 0) {
            return 0.0;
        }
        if ((size & 1) == 0) {
            return (max.last() + min.first()) / 2.0;
        }else {
            return (double)max.last();
        }
    }
}
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5};
public static ArrayList<Integer> getMaxInWindow(int[] nums,int k){
ArrayList<Integer> res = new ArrayList<Integer>();
if(nums == null || nums.length < k || k < 1)
return res;
LinkedList<Integer> queue = new LinkedList<Integer>();
for(int i = 0; i < k - 1; i++){
while(!queue.isEmpty() && nums[i] > nums[queue.getLast()])
queue.removeLast();
queue.addLast(i);
}
int len = nums.length;
for(int i = k - 1; i < len; i++){
while(!queue.isEmpty() && nums[i] > nums[queue.getLast()])
queue.removeLast();
queue.addLast(i);
if(i - queue.getFirst() == k)
queue.removeFirst();
res.add(nums[queue.getFirst()]);
}
return res;
}

1.矩阵中的路径(网上的)

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 a b c e s f c s a d e e 矩阵中包含一条字符串"bccced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

解答:

public class Solution {

    public boolean hasPath(char[] matrix, int rows, int cols, char[] str){  

        //创建一个布尔数组  

        boolean visited[]= new boolean[matrix.length];  

        for(int i = 0;i<rows;i++){  

            for(int j=0;j<cols;j++){  

                if(hasPathCore(matrix,i,j,0,rows,cols,str,visited)){  

                    return true;  

                }  

            }  

        }  

        return false;  

    }  

      

   public boolean hasPathCore(char[] matrix,int i,int j,int k,int rows,int cols,char[] str,boolean[] visited){  

        // 对于给定行数和列数得到其在矩阵中的下标  

        int index = i*cols+j;  

        //判断合法性  

        if(i<0||i>=rows||j<0||j>=cols||matrix[index]!=str[k]||visited[index]){  

            return false;  

        }  

        visited[index] = true;  

        // 如果递归到最后一个位置的字符,则表明前面位置的字符都在矩阵中找到了对应的位置  

        if (k == str.length - 1)  

            return true;  

        if(hasPathCore(matrix,i-1,j,k+1,rows,cols,str,visited)  

                ||hasPathCore(matrix,i,j-1,k+1,rows,cols,str,visited)  

                ||hasPathCore(matrix,i+1,j,k+1,rows,cols,str,visited)  

                ||hasPathCore(matrix,i,j+1,k+1,rows,cols,str,visited)){  

                return true;  

        }else{  

            //如果相邻格子的字符都没有匹配到下一个字符,则需要回到前一个格子,从而需要把把位置的状态重新设定为未访问  

            k--;  

            visited[index] = false;  

        }  

        return false;  

    }  

}

2.机器人的运动范围

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

解答:

import java.util.Stack;  

public class Solution {  

     public int movingCount(int threshold, int rows, int cols) {

        // 创建一个数组记录一个格子是否被访问

        boolean[][] visited = new boolean[rows][cols];

        return movingCountCore(threshold, rows, cols, 0, 0, visited);

    }

    private int movingCountCore(int threshold, int rows, int cols, int i,int j, boolean[][] visited) {

        int count = 0;

        // 异常处理

        if (i < 0 || i >= rows || j < 0 || j >= cols

                || numberIndexCount(i) + numberIndexCount(j) > threshold

                || visited[i][j])

            return 0;

        visited[i][j] = true;

        count = 1 + movingCountCore(threshold, rows, cols, i - 1, j, visited)

                  + movingCountCore(threshold, rows, cols, i + 1, j, visited)

                  + movingCountCore(threshold, rows, cols, i, j - 1, visited)

                  + movingCountCore(threshold, rows, cols, i, j + 1, visited);

        return count;

    }

    // 用于计算每个坐标的数位之和

    private int numberIndexCount(int number) {

        int sum = 0;

        while (number > 0) {

            sum += number % 10;

            number /= 10;

        }

        return sum;

    }

    public static void main(String[] args) {

        int sum = new Solution().movingCount(5, 10, 10);

        System.out.println(sum);

    }

}

    import java.util.Stack;  

    public class Solution {  

      

        int movingCount(int threshold, int rows, int cols){  

            if(threshold<=0) return 0;  

            Stack<Integer> stack_x = new Stack<Integer>();  

            Stack<Integer> stack_y = new Stack<Integer>();  

            boolean flag[][] = new boolean[rows][cols];       

            stack_x.push(0);stack_y.push(0);  

            int count = 0;  

            int x = 0;int y = 0;flag[0][0] = true;  

            while(stack_x.size()!=0 && stack_y.size()!=0){  

                x = stack_x.peek();y = stack_y.peek();  

                //上  

                if((x-1)>=0 && !flag[x-1][y] && checkGo(threshold, x-1, y)){  

                    stack_x.add(x-1);  

                    stack_y.add(y);  

                    flag[x-1][y] = true;  

                    continue;  

                }  

                //下  

                if((x+1)<rows && !flag[x+1][y] && checkGo(threshold, x+1, y)){  

                    stack_x.add(x+1);  

                    stack_y.add(y);  

                    flag[x+1][y]=true;  

                    continue;  

                }  

                //左  

                if((y-1)>=0 && !flag[x][y-1] && checkGo(threshold, x, y-1)){  

                    stack_x.add(x);  

                    stack_y.add(y-1);  

                    flag[x][y-1]=true;  

                    continue;  

                }  

                //右           

                if((y+1)<cols && !flag[x][y+1] && checkGo(threshold, x, y+1)){                 

                    stack_x.add(x);  

                    stack_y.add(y+1);  

                    flag[x][y+1]=true;  

                    continue;  

                }  

                stack_x.pop();stack_y.pop();  

                count++;  

            }  

            return count;         

        }  

          

        boolean checkGo(int k,int rows,int cols){  

            int n = 0;  

            while(rows>0){  

                n += rows%10;  

                rows /= 10;  

            }  

            while(cols>0){  

                n += cols%10;  

                cols /= 10;  

            }  

            if(n>k) return false;  

            return true;  

        }  

}  

66.序列化二叉树

请实现两个函数,分别用来序列化和反序列化二叉树。

public class Solution {

     public int index = -1;

    String Serialize(TreeNode root) {

        StringBuffer sb = new StringBuffer();

        if(root == null){

            sb.append("#,");

            return sb.toString();

        }

        sb.append(root.val + ",");

        sb.append(Serialize(root.left));

        sb.append(Serialize(root.right));

        return sb.toString();

  }

    TreeNode Deserialize(String str) {

        index++;

       int len = str.length();

        if(index >= len){

            return null;

        }

        String[] strr = str.split(",");

        TreeNode node = null;

        if(!strr[index].equals("#")){

            node = new TreeNode(Integer.valueOf(strr[index]));

            node.left = Deserialize(str);

            node.right = Deserialize(str);

        }

         

        return node;

  }

}

  • 1
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值