劍指67題解

劍指offer

需要md私信

1 二維數組中的查找

二維數組中的查找

在一個二維數組中(每個一維數組的長度相同),每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。

思路:從右上角開始往左下角塞選

public class select2DArry {
    public boolean Find(int target, int[][] array) {
        int m = array.length;
        if (m == 0) throw new IllegalArgumentException();
        int n = array[0].length;

        for (int i = 0, j = n - 1; i < m && j >= 0; ) {
            if (array[i][j] == target) {
                return true;
            } else if (array[i][j] < target) i++;
            else j--;
        }
        return false;
    }
}

2 替換空格

請實現一個函數,將一個字符串中的每個空格替換成“%20”。例如,當字符串爲We Are Happy.則經過替換之後的字符串爲We%20Are%20Happy。

替換空格

public class Solution {
    public String replaceSpace(StringBuffer str) {
        if(str == null || str.toString().equals(""))return "";
        int count = 0;
		for(char c : str.toString().toCharArray())
            if(c == ' ')
                count++;
        char []res = new char[str.length() + 2 * count];
        int i = res.length - 1, j = str.length() - 1;
        while(i >= 0){
            char c = str.charAt(j --);
            if(c != ' '){
                res[i --] = c;
            }else{
                res[i--] = '0';
                res[i--] = '2';
                res[i--] = '%';
            }
        }
        return new String(res);
    }
}

3 從尾到頭打印鏈表

輸入一個鏈表,按鏈表從尾到頭的順序返回一個ArrayList。

從尾到頭打印鏈表

public class PrintList {
    ArrayList<Integer> res = new ArrayList<>();

    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        if (listNode != null) {
            printListFromTailToHead(listNode.next);
            res.add(listNode.val);
        }
        return res;
    }
}

4 重建二叉樹

輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。

重建二叉樹

public class BuildTree {
    public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
        int preL = pre.length, inL = in.length;
        if (pre == null || in == null || preL != inL)
            throw new IllegalArgumentException();

        return build(pre, 0, preL - 1, in, 0, inL - 1);
    }

    //{1,2,4,7,3,5,6,8} 前序遍歷  1  是根 (第一個元素)
    //{4,7,2,1,5,3,8,6}    {4,7,2           (左孩子)1(右孩子)           5,3,8,6} 進行遞歸
    private TreeNode build(int[] pre, int ps, int pe, int[] in, int is, int ie) {
        if (ps > pe || is > ie) return null;

        TreeNode root = new TreeNode(pre[ps]);
        for (int i = is; i <= ie; i++) {
            if (pre[ps] == in[i]) {
                root.left = build(pre, ps + 1, i - is + ps, in, is, i - 1);
                root.right = build(pre, i - is + ps + 1, pe, in, i + 1, ie );
                break;
            }
        }
        return root;
    }
}

5 兩個棧實現隊列

用兩個棧來實現一個隊列,完成隊列的Push和Pop操作。 隊列中的元素爲int類型。

用兩個棧實現隊列

public class Stack2Queue {
    public class Solution {
        Stack<Integer> stack1 = new Stack<Integer>();
        Stack<Integer> stack2 = new Stack<Integer>();

        public void push(int node) {
            stack1.push(node);
        }

        public int pop() {
            if(!stack2.isEmpty()) return stack2.pop();
            if(stack1.isEmpty()) throw new NullPointerException();
            while(!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
            return stack2.pop();
        }
    }
}

6 旋轉數組的最小數字

把一個數組最開始的若干個元素搬到數組的末尾,我們稱之爲數組的旋轉。
輸入一個非遞減排序的數組的一個旋轉,輸出旋轉數組的最小元素。
例如數組{3,4,5,1,2}爲{1,2,3,4,5}的一個旋轉,該數組的最小值爲1。
NOTE:給出的所有元素都大於0,若數組大小爲0,請返回0。

旋轉數組的最小數字

思路:二分搜索,如果大於說明這個元素一定不是最小,如果小於說明有可能,如果等於則爲重複數字,向左(遞減)消除重複數字

public class minNumber {
    public int minNumberInRotateArray(int [] array) {
        if(array.length == 0) return 0;

        int l = 0, r = array.length - 1;
        while(l < r) {
            int mid  = l - (l - r) / 2;
            if(array[mid] > array[r]) l = mid + 1;
            else if(array[mid] == array[r]) r --;
            else r = mid;
        }
        return array[l];
    }
}

7 斐波那契數列

大家都知道斐波那契數列,現在要求輸入一個整數n,請你輸出斐波那契數列的第n項(從0開始,第0項爲0)。n<=39

斐波那契數列

  • 解法一:使用記憶化搜索
/**
 * f(n) = f(n - 1) + f(n - 2)
 */
public class Fibonacci {
    int memo[];
    public int Fibonacci(int n) {
        memo = new int[n + 1];
        return helper(n);
    }
    private int helper(int n) {
        if(n < 2 )return n;
        if(memo[n] != 0) return memo[n];
        memo[n] = helper(n - 1) + helper(n - 2);
        return memo[n];
    }
}
  • 解法二:動態規劃
public int Fibonacci2(int n) {
    if(n < 2 )return n;
    return n;
    int fn1 = 0;
    int fn2 = 1;
    int f;
    for(int i = 2 ; i <= n ; i++){
        f = fn2;
        fn2 = fn2 + fn1;
        fn1 = f;
    }
    return fn2;
}

8 跳臺階

一隻青蛙一次可以跳上1級臺階,也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法(先後次序不同算不同的結果)。

跳臺階

同上斐波那契數列一致

9 變態跳臺階

一隻青蛙一次可以跳上1級臺階,也可以跳上2級……它也可以跳上n級。求該青蛙跳上一個n級的臺階總共有多少種跳法。

變態跳臺階

/**
f(1) = 1
f(2) = f(2-1) + f(2-2)         //f(2-2) 表示2階一次跳2階的次數。
f(3) = f(3-1) + f(3-2) + f(3-3)
...
f(n) = f(n-1) + f(n-2) + f(n-3) + ... + f(n-(n-1)) + f(n-n)
f(n) = f(n-1)+f(n-2)+...+f(n-(n-1)) + f(n-n) => f(0) + f(1) + f(2) + f(3) + ... + f(n-1)

f(n-1) = f(0) + f(1)+f(2)+f(3) + ... + f((n-1)-1) = f(0) + f(1) + f(2) + f(3) + ... + f(n-2)
f(n) = f(0) + f(1) + f(2) + f(3) + ... + f(n-2) + f(n-1) = f(n-1) + f(n-1) = 2*f(n-1)
**/

public class Solution {
    public int JumpFloorII(int target) {
        if (target <= 0) {
            return -1;
        } else if (target == 1) {
            return 1;
        } else {
            return 2 * JumpFloorII(target - 1);
        }
    }
}

10 矩陣覆蓋

我們可以用2 * 1的小矩形橫着或者豎着去覆蓋更大的矩形。請問用n個2 *1的小矩形無重疊地覆蓋一個2 * n的大矩形,總共有多少種方法?

矩形覆蓋

同理斐波那數列

public class Solution {
    public int RectCover(int target) {
        if(target == 0)return 0;
        if(target == 1)return 1;
        if(target == 2)return 2;
        int memo[] = new int[target + 1];
        memo[1] = 1;
        memo[2] = 2;
        for(int i = 3; i <= target; i ++ ){
            memo[i] = memo[i - 1] + memo[i - 2];
        }
        return memo[target];
    }
}

11 二進制中1的個數

輸入一個整數,輸出該數二進制表示中1的個數。其中負數用補碼錶示。

二進制中1的個數

//一個數 & (這個數 - 1) 會消除掉這個數二進制的最右的一個1
public class Solution {
    public int NumberOf1(int n) {
        int count = 0;
        while(n != 0) {
            n &= n - 1;
            count ++;
        }
        return count;
    }
}

12 數值的整數次方

給定一個double類型的浮點數base和int類型的整數exponent。求base的exponent次方。保證base和exponent不同時爲0

數值的整數次方

public class Solution {
    public double Power(double base, int exponent) {
        if(exponent == 0)return 1;
        if(exponent == 1)return base;
        //先不考慮exponent的正負  
        int n = exponent < 0 ? -exponent : exponent;
        //任何大於1的指數都可以化成兩個指數相加  奇數的話最後在多乘一遍
        double res = Power(base,n >> 1);
        
        res = res * res;
        
        if((n & 1) == 1)
            res *= base;
        //如果指數小於0,則化成分數
        if(exponent < 0)
            res = 1 / res;
        return res;
  }
}

13 調整數組順序使奇數位於偶數前面

輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得所有的奇數位於數組的前半部分,所有的偶數位於數組的後半部分,並保證奇數和奇數,偶數和偶數之間的相對位置不變。

調整數組順序使奇數位於偶數前面

思路:使用冒泡排序,將偶數下沉到後面

public class Solution {
    //使用冒泡算法  前偶後奇就交換
    public void reOrderArray(int[] array) {
        for (int i = 0; i < array.length; i++) {
            for (int j = 1; j < array.length - i; j++) {
                //前一個奇數,後一個偶數就互換位置
                if (array[j - 1] % 2 == 0 &&
                        array[j] % 2 != 0) {
                    int temp = array[j];
                    array[j] = array[j - 1];
                    array[j - 1] = temp;
                }
            }
        }
    }
}

14 鏈表中倒數第k個結點

輸入一個鏈表,輸出該鏈表中倒數第k個結點。

鏈表中倒數第k個結點

思路:先讓快指針走k步,之後和起點指針一起走,快指針到達尾部,慢指針的位置就是倒數第k個節點

public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        if(head == null) return null;
        ListNode fast = head;
        ListNode pre = new ListNode(0);
        pre.next = head;
        
        while(k -- > 0) {
            if(fast == null) return null;
            fast = fast.next;
        }
        
        while(fast != null) {
            fast = fast.next;
            pre = pre.next;
        }
        // 如果要刪除倒數第k個節點
        // pre.next = pre.next.next;
        return pre.next;
    }
}

15 反轉鏈表

輸入一個鏈表,反轉鏈表後,輸出新鏈表的表頭。

反轉鏈表

public class Solution {
    public ListNode ReverseList(ListNode head) {
        if(head == null)return null;
        ListNode pre = null;
        ListNode next;
        
        while(head != null) {
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }
}

16 合併兩個排序的鏈表

輸入兩個單調遞增的鏈表,輸出兩個鏈表合成後的鏈表,當然我們需要合成後的鏈表滿足單調不減規則。

合併兩個排序的鏈表

public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1 == null) return list2;
        if(list2 == null)  return list1;
        ListNode head;
        if(list1.val > list2.val) {
            head = list2;
            head.next = Merge(list1,list2.next);
        } else {
            head = list1;
            head.next = Merge(list1.next,list2);
        }
        return head;
    }
}

17 樹的子結構

輸入兩棵二叉樹A,B,判斷B是不是A的子結構。(ps:我們約定空樹不是任意一個樹的子結構)

樹的子結構

public class Solution {
    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        if(root1 == null || root2 == null)return false;
        return isSubTree(root1,root2) || HasSubtree(root1.left,root2) ||
            HasSubtree(root1.right,root2);
    }
    
    private boolean isSubTree(TreeNode node1, TreeNode node2) {
        if(node2 == null)return true;
        if(node1 == null)return false;
        return node1.val == node2.val && isSubTree(node1.left,node2.left) && 
            isSubTree(node1.right,node2.right);
    }
}

18 二叉樹的鏡像

操作給定的二叉樹,將其變換爲源二叉樹的鏡像。

二叉樹的鏡像

public class Solution {
    public void Mirror(TreeNode root) {
        if(root != null){
            TreeNode temp = root.left;
            root.left = root.right;
            root.right = temp;
            Mirror(root.left);
            Mirror(root.right);
        }
    }
}

19 順時針打印矩陣

順時針打印矩陣

public class Solution {
    public ArrayList<Integer> printMatrix(int[][] matrix) {
        if (matrix.length == 0)
            return new ArrayList<>();
        ArrayList<Integer> res = new ArrayList<>();

        int index = 0;
        int row = matrix.length;
        int col = matrix[0].length;

        while (index * 2 < row && index * 2 < col) {
            printMatrix(res, matrix, index, row, col);
            ++index;
        }
        return res;
    }

    private void printMatrix(List<Integer> res, int[][] matrix, int index, int row, int col) {
        row = row - index - 1;
        col = col - index - 1;
        for (int i = index; i <= col; i++) {
            res.add(matrix[index][i]);
        }
        if (index < row) {
            for (int i = index + 1; i <= row; i++) {
                res.add(matrix[i][col]);
            }
        }
        if (index < row && index < col) {
            for (int i = col - 1; i >= index; i--) {
                res.add(matrix[row][i]);
            }
        }

        if (index < col && row - index > 1) {
            for (int i = row - 1; i >= index + 1; i--) {
                res.add(matrix[i][index]);
            }
        }
    }
}

20 包含min函數的棧

定義棧的數據結構,請在該類型中實現一個能夠得到棧中所含最小元素的min函數(時間複雜度應爲O(1))。

注意:保證測試中不會當棧爲空的時候,對棧調用pop()或者min()或者top()方法。

包含min函數的棧

public class Solution {

    int min = Integer.MAX_VALUE;
    Stack<Integer> stack = new Stack<>();

    //保存上一個最小值
    public void push(int node) {
        if (node < min) {
            stack.push(min);
            min = node;
        }
        stack.push(node);
    }

    //如果彈出的是最小值,則設置上一個最小值
    public void pop() {
        if (min == stack.pop())
            min = stack.pop();
    }

    public int top() {
        return stack.peek();
    }

    public int min() {
        return min;
    }
}

21 棧的壓入、彈出序列

輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否可能爲該棧的彈出順序。假設壓入棧的所有數字均不相等。例如序列1,2,3,4,5是某棧的壓入順序,序列4,5,3,2,1是該壓棧序列對應的一個彈出序列,但4,3,5,1,2就不可能是該壓棧序列的彈出序列。(注意:這兩個序列的長度是相等的)

棧的壓入、彈出序列

public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
        if(pushA.length != popA.length)return false;
        Stack<Integer> stack = new Stack<>();
        for(int i = 0,j = 0; i < pushA.length; i ++) {
            stack.push(pushA[i]);
            while(!stack.isEmpty() && stack.peek() == popA[j]) {
                stack.pop();
                j ++;
            }
        }
        return stack.isEmpty();
    }
}

22 從上往下打印二叉樹

從上往下打印出二叉樹的每個節點,同層節點從左至右打印。

從上往下打印二叉樹

public class Solution {
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> res = new ArrayList<>();
        if(root == null)return res;
        Queue<TreeNode> q = new LinkedList<>();
        q.add(root);
        
        while(!q.isEmpty()) {
            TreeNode node = q.remove();
            res.add(node.val);
            if(node.left != null) q.add(node.left);
            if(node.right != null) q.add(node.right);
        }
        return res;
    }
}

23 二叉搜索樹的後序遍歷序列

輸入一個整數數組,判斷該數組是不是某二叉搜索樹的後序遍歷的結果。如果是則輸出Yes,否則輸出No。假設輸入的數組的任意兩個數字都互不相同

二叉搜索樹的後序遍歷序列

思路:BST的後序序列的合法序列是,對於一個序列S,最後一個元素是x (也就是根),如果去掉最後一個元素的序列爲T,那麼T滿足:T可以分成兩段,前一段(左子樹)小於x,後一段(右子樹)大於x,且這兩段(子樹)都是合法的後序序列

public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence.length == 0)return false;
        return helper(sequence, 0, sequence.length - 1);
    }
    
    private boolean helper(int[] sequence, int l, int r) {
        int rootIndex = r;
        if(l < r) {
            //找到分割點
            while(l < r && sequence[rootIndex] <= sequence[r]) r--;
            for(int i = l; i < r; i ++) {
                if(sequence[i] >= sequence[rootIndex])return false;
            }
            return helper(sequence, l, r) && helper(sequence, r, rootIndex - 1);
        }
        return true;
    }
}

24 二叉樹中和爲某一值的路徑

輸入一顆二叉樹的根節點和一個整數,打印出二叉樹中結點值的和爲輸入整數的所有路徑。路徑定義爲從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。(注意: 在返回值的list中,數組長度大的數組靠前)

二叉樹中和爲某一值的路徑

public class Solution {
    ArrayList<ArrayList<Integer>> res;
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
        res = new ArrayList<>();
        if(root == null) return res;
        helper(root, new ArrayList<>(), 0, target);
        return res;
    }
    private void helper(TreeNode node, ArrayList<Integer> list, int sum,int target) {
        list.add(node.val);
        sum += node.val;
        //滿足兩個條件,一個是必須到根節點,一個是target滿足
        if(sum == target && node.left == null && node.right == null) {
            res.add(new ArrayList<>(list));
        }
        if(node.left != null) helper(node.left, list, sum, target);
        if(node.right != null) helper(node.right, list, sum, target);
        //在滿足之後也需要回溯
        list.remove(list.size() - 1);
    }
}

25 複雜鏈表的複製

輸入一個複雜鏈表(每個節點中有節點值,以及兩個指針,一個指向下一個節點,另一個特殊指針指向任意一個節點),返回結果爲複製後複雜鏈表的head。(注意,輸出結果中請不要返回參數中的節點引用,否則判題程序會直接返回空)

複雜鏈表的複製

public class Solution {
    public RandomListNode Clone(RandomListNode head)
    {
        if(head == null)return null;
        RandomListNode node = head;
        //複製普通節點  直接擴展一倍
        while(node != null){
            RandomListNode dummyNode = new RandomListNode(node.label);
            dummyNode.next = node.next; 
            node.next = dummyNode;
            node = dummyNode.next;
        }
        //複製隨機節點
        RandomListNode node2 = head;
        while(node2 != null){
            if(node2.random != null)
                node2.next.random = node2.random.next;
            node2 = node2.next.next;
        }
        //分裂鏈表
        RandomListNode cHead = head.next;
        RandomListNode node3 = cHead;
        RandomListNode p = head;
        p.next = node3.next;
        p = p.next;
        
        while(p != null){
            node3.next = p.next;
            node3 = node3.next;
            
            p.next = node3.next;
            p = p.next;
        }
        
        return cHead;
    }
}

26 二叉搜索樹與雙向鏈表

輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。

二叉搜索樹與雙向鏈表

public class Solution {
    TreeNode head = null;
    TreeNode end = null;
    public TreeNode Convert(TreeNode pRootOfTree) {
        ConvertSub(pRootOfTree);
        return head;
    }
    //直接終須遍歷
    public void ConvertSub(TreeNode pRootOfTree) {
        if(pRootOfTree == null)
            return ;
        Convert(pRootOfTree.left);
        //第一個節點記錄頭尾指針
        if(end == null){
            head = pRootOfTree;
            end = pRootOfTree;
        }else{
            //不斷的向後移動尾節點,直到最後一個節點
            end.right = pRootOfTree;
            pRootOfTree.left = end;
            end = pRootOfTree;
        }
        Convert(pRootOfTree.right);
    }
}

27 字符串的排列

輸入一個字符串,按字典序打印出該字符串中字符的所有排列。例如輸入字符串abc,則打印出由字符a,b,c所能排列出來的所有字符串abc,acb,bac,bca,cab和cba。

字符串的排列

public class Permutation {
    ArrayList<String> res;
    boolean exist[];

    public ArrayList<String> Permutation(String str) {
        res = new ArrayList<>();
        if (str == null || "".equals(str)) return res;
        exist = new boolean[str.length() + 1];
        char[] chars = str.toCharArray();
        Arrays.sort(chars);
        helper(new String(chars), "");
        return res;
    }

    private void helper(String str, String t) {
        if (str.length() == t.length()) {
            res.add(t);
            return;
        }
        for (int i = 0; i < str.length(); i++) {
            if (!exist[i]) {
                //消除重複字母
                if (i > 0 && str.charAt(i) == str.charAt(i - 1) && !exist[i - 1])
                    continue;
                exist[i] = true;
                t += str.charAt(i);
                helper(str, t);
                t = t.substring(0, t.length() - 1);
                exist[i] = false;
            }
        }
    }
}

28 數組中出現次數超過一半的數字

數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如輸入一個長度爲9的數組{1,2,3,2,2,2,5,4,2}。由於數字2在數組中出現了5次,超過數組長度的一半,因此輸出2。如果不存在則輸出0。

數組中出現次數超過一半的數字

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        HashMap<Integer,Integer> map = new HashMap<>();
        int max = array.length/2 + 1;
        for(int i = 0; i < array.length; i++)
        {
            int k = array[i];
            int count = map.getOrDefault(k,0);
            if(count + 1 == max)
                return k;
            else
                map.put(k,count + 1);
        }
        return 0;
    }
}

29 最小的K個數

輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,。

最小的K個數

  • 解法一:使用最大堆(nlog(k))
public class Solution {
   public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
       ArrayList<Integer> result = new ArrayList<Integer>();
       int length = input.length;
       if(k > length || k == 0){
           return result;
       }
        PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(k, new Comparator<Integer>() {
 
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2.compareTo(o1);
            }
        });
        for (int i = 0; i < length; i++) {
            if (maxHeap.size() != k) {
                maxHeap.offer(input[i]);
            } else if (maxHeap.peek() > input[i]) {
                Integer temp = maxHeap.poll();
                temp = null;
                maxHeap.offer(input[i]);
            }
        }
        for (Integer integer : maxHeap) {
            result.add(integer);
        }
        return result;
    }
}
  • 解法二:partition思想 O(n)
    public ArrayList<Integer> GetLeastNumbers_Solution1(int[] input, int k) {
        ArrayList<Integer> leastNumbers = new ArrayList<Integer>();
        while (input == null || k <= 0 || k > input.length)
            return leastNumbers;
        int start = 0;
        int end = input.length - 1;
        int index = partition(input, start, end);
        while (index != k - 1) {
            if (index < k - 1) {
                start = index + 1;
                index = partition(input, start, end);
            } else {
                end = index - 1;
                index = partition(input, start, end);
            }
        }
        for (int i = 0; i < k; i++) {
            leastNumbers.add(input[i]);
        }
        return leastNumbers;
    }

    private int partition(int[] arr, int start, int end) {
        int pivotKey = arr[start];
        while (start < end) {
            while (start < end && arr[end] >= pivotKey)
                end--;
            swap(arr, start, end);
            while (start < end && arr[start] <= pivotKey)
                start++;
            swap(arr, start, end);
        }
        return start;
    }

    private void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

30 連續子數組的最大和

連續子數組的最大和

public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        if(array == null || array.length == 0)return 0;
        int res = array[0];
        int max = array[0];
        for(int i = 1; i < array.length; i ++) {
            max = Math.max(array[i], max + array[i]);
            res = Math.max(max, res);
        }
        return res;
    }
}

31 整數中1出現的次數(從1到n整數中1出現的次數)(難)

求出113的整數中1出現的次數,並算出1001300的整數中1出現的次數?爲此他特別數了一下1~13中包含1的數字有1、10、11、12、13因此共出現6次,但是對於後面問題他就沒轍了。ACMer希望你們幫幫他,並把問題更加普遍化,可以很快的求出任意非負整數區間中1出現的次數(從1 到 n 中1出現的次數)。

整數中1出現的次數(從1到n整數中1出現的次數)

public class Solution {
    int countDigitOne(int n) {
        int ones = 0;
        for (long long m = 1; m <= n; m *= 10) {
            int a = n/m, b = n%m;
            ones += (a + 8) / 10 * m + (a % 10 == 1) * (b + 1);
        }
        return ones;
    }
}

32 把數組排成最小的數

輸入一個正整數數組,把數組裏所有數字拼接起來排成一個數,打印能拼接出的所有數字中最小的一個。例如輸入數組{3,32,321},則打印出這三個數字能排成的最小數字爲321323。

把數組排成最小的數

public class Solution {
    public String PrintMinNumber(int [] numbers) {
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i < numbers.length; i++) {
            list.add(numbers[i]);
        }
        Collections.sort(list, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
              String s1 = o1 + "" + o2;
              String s2 = o2 + "" + o1;
              return s1.compareTo(s2);
            }
        });
        String res = "";
        for (int i = 0; i < list.size(); i++) {
            res += list.get(i);
        }
				return res;
    }
}

33 醜數

把只包含質因子2、3和5的數稱作醜數(Ugly Number)。例如6、8都是醜數,但14不是,因爲它包含質因子7。 習慣上我們把1當做是第一個醜數。求按從小到大的順序的第N個醜數。

醜數

public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if(index <= 0)return 0;
        List<Integer> list = new ArrayList<>();
        list.add(1);
        int i2 = 0, i3 = 0, i5 = 0;
        
        while(list.size() < index) {
            int t2 = list.get(i2) * 2;
            int t3 = list.get(i3) * 3;
            int t5 = list.get(i5) * 5;
            int min = Math.min(t2, Math.min(t3, t5));
            if(t2 == min)i2 ++;
            if(t3 == min)i3 ++;
            if(t5 == min)i5 ++;
            list.add(min);
        }
        return list.get(index - 1);
    }
}

34 第一個只出現一次的字符位置

在一個字符串(0<=字符串長度<=10000,全部由字母組成)中找到第一個只出現一次的字符,並返回它的位置, 如果沒有則返回 -1(需要區分大小寫).

第一個只出現一次的字符位置

public class Solution {
    	public int FirstNotRepeatingChar(String str) {
		HashMap<Character,Integer> map = new HashMap<>();
		for (int i = 0; i < str.length(); i++) {
			char c = str.charAt(i);
			int count = map.getOrDefault(c, 0);
			map.put(c, count + 1);
		}
		
		for (int i = 0; i < str.length(); i++) {
			char c = str.charAt(i);
			if(map.get(c) == 1)
				return i;
		}
		return -1;
    }
}

35 數組中的逆序對(難)

在數組中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數P。並將P對1000000007取模的結果輸出。 即輸出P%1000000007

數組中的逆序對

public class Solution {
    public int InversePairs(int [] array) {
        if(array==null||array.length==0)
        {
            return 0;
        }
        int[] copy = new int[array.length];
        for(int i=0;i<array.length;i++)
        {
            copy[i] = array[i];
        }
        int count = InversePairsCore(array,copy,0,array.length-1);//數值過大求餘
        return count;
         
    }
    private int InversePairsCore(int[] array,int[] copy,int low,int high)
    {
        if(low==high)
        {
            return 0;
        }
        int mid = (low+high)>>1;
        int leftCount = InversePairsCore(array,copy,low,mid)%1000000007;
        int rightCount = InversePairsCore(array,copy,mid+1,high)%1000000007;
        int count = 0;
        int i=mid;
        int j=high;
        int locCopy = high;
        while(i>=low&&j>mid)
        {
            if(array[i]>array[j])
            {
                count += j-mid;
                copy[locCopy--] = array[i--];
                if(count>=1000000007)//數值過大求餘
                {
                    count%=1000000007;
                }
            }
            else
            {
                copy[locCopy--] = array[j--];
            }
        }
        for(;i>=low;i--)
        {
            copy[locCopy--]=array[i];
        }
        for(;j>mid;j--)
        {
            copy[locCopy--]=array[j];
        }
        for(int s=low;s<=high;s++)
        {
            array[s] = copy[s];
        }
        return (leftCount+rightCount+count)%1000000007;
    }
}

36 兩個鏈表的第一個公共結點

輸入兩個鏈表,找出它們的第一個公共結點。(注意因爲傳入數據是鏈表,所以錯誤測試數據的提示是用其他方式顯示的,保證傳入數據是正確的)

兩個鏈表的第一個公共結點

思路:

假定 List1長度: a+n List2長度:b+n, 且 a<b那麼 p1 會先到鏈表尾部, 這時p2 走到 a+n位置,將p1換成List2頭部接着p2再走b+n-(n+a) =b-a 步到鏈表尾部,這時p1也走到List2b-a位置,還差a步就到可能的第一個公共節點。將p2換成 List1頭部,p2走a步也到可能的第一個公共節點。如果恰好p1==p2,那麼p1就是第一個公共節點。 或者p1和p2一起走n步到達列表尾部,二者沒有公共節點,退出循環。 同理a>=b.時間複雜度O(n+a+b)

public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode p = pHead1;
        ListNode q = pHead2;
        
        while(q != p) {
            if(q != null) {
                q = q.next;
            } else {
                q = pHead1;
            }
            
            if(p != null) {
                p = p.next;
            } else {
                p = pHead2;
            }
        }
        return q;
    }
}

37 數字在排序數組中出現的次數

統計一個數字在排序數組中出現的次數。

數字在排序數組中出現的次數

思路:使用二分搜索,之後像左右搜索數量

public class Solution {
    public int GetNumberOfK(int [] arr , int k) {
        int index = binarySearch(arr,0,arr.length - 1,k);
        if(index == -1)return 0;
        int times = 1;
        int l = index - 1;
        int r = index + 1;
        while(r < arr.length && arr[r ++] == k)times ++;
        while(l >= 0 && arr[l --] == k)times ++;
        return times;
    }
    public int binarySearch(int arr[],int l,int r,int target){
        while(l <= r){
            int mid = (l + r)/2;
            if(arr[mid] == target){
                return mid;
            }else if(arr[mid] < target)l = mid + 1;
            else r = mid - 1;
        }
        return -1;
    }
}

38 二叉樹的深度

輸入一棵二叉樹,求該樹的深度。從根結點到葉結點依次經過的結點(含根、葉結點)形成樹的一條路徑,最長路徑的長度爲樹的深度。

二叉樹的深度

public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root == null) return 0;
        return Math.max(TreeDepth(root.left), TreeDepth(root.right)) + 1;
    }
}

39 平衡二叉樹

輸入一棵二叉樹,判斷該二叉樹是否是平衡二叉樹。

平衡二叉樹

public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        if(root == null)return true;
        return IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right)
            && Math.abs(TreeDepth(root.right) - TreeDepth(root.left)) < 2;
    }
    
    public int TreeDepth(TreeNode root) {
        if(root == null) return 0;
        return Math.max(TreeDepth(root.left), TreeDepth(root.right)) + 1;
    }
}

40 數組中只出現一次的數字

一個整型數組裏除了兩個數字之外,其他的數字都出現了兩次。請寫程序找出這兩個只出現一次的數字。

數組中只出現一次的數字

//num1,num2分別爲長度爲1的數組。傳出參數
//將num1[0],num2[0]設置爲返回結果
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        int t = array[0];
        for(int i = 1; i < array.length; i ++) {
            t ^= array[i];
        }
        
        int index = 1;
        while((t & index) == 0) {
            index = index << 1;
        }
        
        for(int i = 0; i < array.length; i ++) {
            if((array[i] & index) == 0) {
                num1[0] ^= array[i];
            }
            else {
                num2[0] ^= array[i];
            }
        }
    }
}

41 和爲S的連續正數序列

輸出所有和爲S的連續正數序列。序列內按照從小至大的順序,序列間按照開始數字從小到大的順序

和爲S的連續正數序列

思路:使用滑動窗口

public class Solution {
    public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> allList = new ArrayList<>();
        int left = 1, right = 2;
        while (left < right) {
            int toSum = (left + right) * (right - left + 1) / 2;
            if (toSum == sum) {
                ArrayList<Integer> list = new ArrayList<>();
                for (int i = left; i <= right; i++) {
                    list.add(i);
                }
                allList.add(list);
                left++;
            } else if (toSum < sum) right++;
            else left++;
        }
        return allList;
    }
}

42 和爲S的兩個數字

輸入一個遞增排序的數組和一個數字S,在數組中查找兩個數,使得他們的和正好是S,如果有多對數字的和等於S,輸出兩個數的乘積最小的。

和爲S的兩個數字

思路:使用滑動窗口

public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> res = new ArrayList<>();
        if(array == null || array.length == 0) return res;
        int l = 0, r = array.length - 1;
        int mul = Integer.MAX_VALUE;
        while(l < r) {
            if(array[l] + array[r] == sum) {
                if(mul > array[l] * array[r]) {
                    res.add(array[l]);
                    res.add(array[r]);
                    mul = array[l] * array[r];
                }
                l ++;
            } else if(array[l] + array[r] > sum) r --;
            else l ++;
        }
        return res;
    }
}

43 左旋轉字符串

彙編語言中有一種移位指令叫做循環左移(ROL),現在有個簡單的任務,就是用字符串模擬這個指令的運算結果。對於一個給定的字符序列S,請你把其循環左移K位後的序列輸出。例如,字符序列S=”abcXYZdef”,要求輸出循環左移3位後的結果,即“XYZdefabc”。是不是很簡單?OK,搞定它!

左旋轉字符串

原理:

假設有字符串s 1 2 3 4 5 6 7左移3位

第一步:反轉0 - n3 2 1 | 4 5 6 7

第二步:反轉 n - s.length3 2 1 | 7 6 5 4

第三步:反轉整個字符串:4 5 6 7 1 2 3

public class Solution {
    public String LeftRotateString(String str,int n) {
        char[] chars = str.toCharArray();        
        if(chars.length < n) return "";
        reverse(chars, 0, n-1);
        reverse(chars, n, chars.length-1);
        reverse(chars, 0, chars.length-1);
        StringBuilder sb = new StringBuilder(chars.length);
        for(char c:chars){
            sb.append(c);
        }
        return sb.toString();
    }
     
    public void reverse(char[] chars,int low,int high){
        char temp;
        while(low<high){
            temp = chars[low];
            chars[low] = chars[high];
            chars[high] = temp;
            low++;
            high--;
        }
    }
}

44 翻轉單詞順序列

反轉單詞輸出順序

翻轉單詞順序列

public class Solution {
    public String ReverseSentence(String str) {
        if(str == null)return null;
        if(str.trim().equals("")){
            return str;
        }
        String strs[] = str.split(" ");
        String res = "";
        for(int i = strs.length - 1; i >= 0; i--){
            res += strs[i];
            if(i != 0)
                res += " ";
        }
        return res;
    }
}

45 撲克牌順子

大小王可以變爲任意數,判斷是否爲順子

撲克牌順子

public class Solution {
    public boolean isContinuous(int [] numbers) {
        if(numbers == null || numbers.length != 5)return false;
        int arr[] = new int[14];
        int min = 14;
        int max = 0;
        for(int i = 0; i < numbers.length; i ++) {
            if(numbers[i] == 0)continue;
            arr[numbers[i]]++;
            if(arr[numbers[i]] > 1)return false;
            if(numbers[i] > max) max = numbers[i];
            if(numbers[i] < min) min = numbers[i];
            if(max - min >= 5) return false;
        }
        return true;
    }
}

46 孩子們的遊戲(圓圈中最後剩下的數

約瑟夫環

孩子們的遊戲(圓圈中最後剩下的數)

約瑟夫環——公式法遞推公式

public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        if(n == 0 || m == 0)return -1;
        int p = 0;
        for(int i = 2; i <= n; i++)
        {
            p = (p + m) % i;
        }
        return p;
    }
}

47 求1+2+3+…+n

求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等關鍵字及條件判斷語句(A?B:C)。

求1+2+3+…+n

思路:利用短路性

public class Solution {
    public int Sum_Solution(int n) {
        int sum = n;
        boolean flag = (n > 0) && ((sum +=Sum_Solution(n - 1)) > 0);
        return sum;
    }
}

48 不用加減乘除做加法

寫一個函數,求兩個整數之和,要求在函數體內不得使用+、-、*、/四則運算符號。

不用加減乘除做加法

public class Solution {
    public int Add(int num1,int num2) {
        if(num2 == 0)return num1;
        int h = (num1 & num2) << 1;
        return Add(num1 ^ num2, h);        
    }
}

49 把字符串轉換成整數

將一個字符串轉換成一個整數,要求不能使用字符串轉換整數的庫函數。 數值爲0或者字符串不是一個合法的數值則返回0

把字符串轉換成整數

public class Solution {
    public int StrToInt(String str) {
        if(str == null ||str.length()==0 || str.trim().equals("")) return 0;
        char[] chars = str.trim().toCharArray();
        int res=0,flag=1,start=0,tmp=0;
        if(chars[0] == '-') {
            flag = -1;
            start = 1;   
        }
        if(chars[0] == '+') start=1;
        for(int i =start;i < chars.length;i++){
            if(chars[i]>'9'||chars[i]<'0')
                return 0;
            //當前個位數字
            int dight = (int)(chars[i]-'0');
            tmp = res*10+dight;
            //判斷正數溢出
            if(flag ==1 && res*10>Integer.MAX_VALUE - dight)
               return 0;
            //判斷負數溢出
            if(flag == -1 && res*10*flag < Integer.MIN_VALUE + dight)
                return 0;
            res = tmp;
        }
         return flag* res;
    }
}

50 數組中重複的數字

在一個長度爲n的數組裏的所有數字都在0到n-1的範圍內。 數組中某些數字是重複的,但不知道有幾個數字是重複的。也不知道每個數字重複幾次。請找出數組中任意一個重複的數字。 例如,如果輸入長度爲7的數組{2,3,1,0,2,5,3},那麼對應的輸出是第一個重複的數字2。

數組中重複的數字

public class Solution {
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        duplication[0] = -1;
        for(int i = 0; i < length; i ++){
            while(numbers[i] != i)
            if(numbers[numbers[i]] == numbers[i]){
                duplication[0] = numbers[i];
                return true;
            }else{
                int temp = numbers[i];
                numbers[i]= numbers[temp];
                numbers[temp] = temp;
            }
        }
        return false;
    }
}

51 構建乘積數組

給定一個數組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[0] = A[1] * A[2] * … * A[n-1],B[n-1] = A[0] * A[1] * … * A[n-2];)

Input:  [1,2,3,4]
Output: [24,12,8,6]

構建乘積數組

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int []res = new int[nums.length];
        res[0] = 1;
        //計算上三角
        for(int i = 1; i < nums.length; i ++){
            res[i] = res[i - 1] * nums[i - 1];
        }
        //計算下三角
        int right = 1;
        for(int i = nums.length - 1; i >= 0 ; i --){
            res[i] = res[i] * right;
            right = nums[i] * right;
        }
        return res;
    }
}

52 正則表達式匹配(難)

請實現一個函數用來匹配包括’.‘和’‘的正則表達式。模式中的字符’.‘表示任意一個字符,而’'表示它前面的字符可以出現任意次(包含0次)。 在本題中,匹配是指字符串的所有字符匹配整個模式。例如,字符串"aaa"與模式"a.a"和"abaca"匹配,但是與"aa.a"和"ab*a"均不匹配

正則表達式匹配

53 表示數值的字符串

請實現一個函數用來判斷字符串是否表示數值(包括整數和小數)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示數值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。

表示數值的字符串

public class Solution {
    private int index = 0;
  
    public boolean isNumeric(char[] str) {
        if (str.length < 1)
            return false;
         
        boolean flag = scanInteger(str);
         
        if (index < str.length && str[index] == '.') {
            index++;
            flag = scanUnsignedInteger(str) || flag;
        }
         
        if (index < str.length && (str[index] == 'E' || str[index] == 'e')) {
            index++;
            flag = flag && scanInteger(str);
        }
         
        return flag && index == str.length;
         
    }
     
    private boolean scanInteger(char[] str) {
        if (index < str.length && (str[index] == '+' || str[index] == '-') )
            index++;
        return scanUnsignedInteger(str);
         
    }
     
    private boolean scanUnsignedInteger(char[] str) {
        int start = index;
        while (index < str.length && str[index] >= '0' && str[index] <= '9')
            index++;
        return start < index; //是否存在整數
    }
}

54 字符流中第一個不重複的字符

請實現一個函數用來找出字符流中第一個只出現一次的字符。例如,當從字符流中只讀出前兩個字符"go"時,第一個只出現一次的字符是"g"。當從該字符流中讀出前六個字符“google"時,第一個只出現一次的字符是"l"。

字符流中第一個不重複的字符

public class Solution {
    //Insert one char from stringstream
    int count[] = new int[128];
    Queue<Character> q = new LinkedList<>();
    public void Insert(char ch)
    {
        count[ch] ++;
        if(count[ch] == 1)
            q.add(ch);
    }
    //return the first appearence once char in current stringstream
    public char FirstAppearingOnce()
    {
        while(!q.isEmpty() && count[q.peek()] >= 2)q.poll();
        if(!q.isEmpty())return q.peek();
        return '#';
    }
}

55 鏈表中環的入口結點

給一個鏈表,若其中包含環,請找出該鏈表的環的入口結點,否則,輸出null。

鏈表中環的入口結點

public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        if(pHead == null || pHead.next == null)return null;
        ListNode p = pHead;
        ListNode q = pHead;
        while(q != null && q.next !=null){
            p = p.next;
            q = q.next.next;
            if(p == q){
                q = pHead;
                while(p != q){
                    p = p.next;
                    q = q.next;
                }
                return q;
            }
        }
        return null;
    }
}

56 刪除鏈表中重複的結點

在一個排序的鏈表中,存在重複的結點,請刪除該鏈表中重複的結點,重複的結點不保留,返回鏈表頭指針。 例如,鏈表1->2->3->3->4->4->5 處理後爲 1->2->5

刪除鏈表中重複的結點

public class Solution {
    public ListNode deleteDuplication(ListNode pHead)
    {
        if(pHead == null)return null;
        ListNode dummy = new ListNode(0);
        dummy.next = pHead;
        ListNode p = dummy;
        ListNode q = pHead;
        while(q != null && q.next != null){
            ListNode next = q.next;
            if(q.val == next.val){
                while(next != null && next.val == q.val){
                    next = next.next;
                }
                p.next = next;
                q = next;
            }else{
                q = q.next;
                p = p.next;
            }
        }
        return dummy.next;
    }
}

57 二叉樹的下一個結點

給定一個二叉樹和其中的一個結點,請找出中序遍歷順序的下一個結點並且返回。注意,樹中的結點不僅包含左右子結點,同時包含指向父結點的指針。

二叉樹的下一個結點

public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        if(pNode == null)return null;
      	//如果節點存在右節點。則下一個節點爲右節點的最左節點
        if(pNode.right != null){
            TreeLinkNode node = pNode.right;
            while(node.left != null)node = node.left;
            return node;
        }
      	//如果不存在右節點
      	//如果節點爲父節點的左節點,下一個節點爲父節點
      	//如果不爲左節點,則找到第一個滿足上面條件的節點
        while(pNode.next != null){
            if(pNode.next.left == pNode)
                return pNode.next;
            pNode = pNode.next;
        }
        return null;
    }
}

58 對稱的二叉樹

請實現一個函數,用來判斷一顆二叉樹是不是對稱的。注意,如果一個二叉樹同此二叉樹的鏡像是同樣的,定義其爲對稱的。

對稱的二叉樹

public class Solution {
    boolean isSymmetrical(TreeNode root)
    {
        if(root == null)return true;
        return isSymmetrical(root.left, root.right);
    }
    
    private boolean isSymmetrical(TreeNode node1, TreeNode node2) {
        if(node1 == null)return node2 == null;
        if(node2 == null)return node1 == null;
        return isSymmetrical(node1.left, node2.right) && isSymmetrical(node1.right,node2.left)
            && node1.val == node2.val;
    }
}

59 按之字形順序打印二叉樹

請實現一個函數按照之字形打印二叉樹,即第一行按照從左到右的順序打印,第二層按照從右至左的順序打印,第三行按照從左到右的順序打印,其他行以此類推。

按之字形順序打印二叉樹

public class Solution {
    public ArrayList<ArrayList<Integer>> Print(TreeNode root) {
        ArrayList<ArrayList<Integer>> res = new ArrayList<>();
        if(root == null)return res;
        Stack<TreeNode> s1 = new Stack<>();
        Stack<TreeNode> s2 = new Stack<>();
        s1.push(root);
        while(!s1.isEmpty() || !s2.isEmpty()) {
            ArrayList<Integer> list = new ArrayList<>();
            if(!s1.isEmpty()) {
                while(!s1.isEmpty()) {
                    TreeNode node = s1.pop();
                    list.add(node.val);
                    if(node.left != null)s2.push(node.left);
                    if(node.right != null)s2.push(node.right);
                }
            } else {
                while(!s2.isEmpty()) {
                    TreeNode node = s2.pop();
                    list.add(node.val);
                    if(node.right != null)s1.push(node.right);
                    if(node.left != null)s1.push(node.left);
                }
            }
            res.add(new ArrayList<>(list));
        }
        return res;
    }
}

60 把二叉樹打印成多行

從上到下按層打印二叉樹,同一層結點從左至右輸出。每一層輸出一行。

把二叉樹打印成多行

public class Solution {
    ArrayList<ArrayList<Integer>> Print(TreeNode root) {
        ArrayList<ArrayList<Integer>> res = new ArrayList<>();
        if(root == null)return res;
        Queue<TreeNode> q = new LinkedList<>();
        q.add(root);
        
        while(!q.isEmpty()) {
            int index = q.size();
            ArrayList<Integer> list = new ArrayList<>();
            while(index -- > 0) {
                TreeNode node = q.remove();
                list.add(node.val);
                if(node.left != null)q.add(node.left);
                if(node.right != null)q.add(node.right);
            }
            res.add(list);
        }
        return res;
    }
}

61 序列化二叉樹

請實現兩個函數,分別用來序列化和反序列化二叉樹二叉樹的序列化是指:把一棵二叉樹按照某種遍歷方式的結果以某種格式保存爲字符串,從而使得內存中建立起來的二叉樹可以持久保存。序列化可以基於先序、中序、後序、層序的二叉樹遍歷方式來進行修改,序列化的結果是一個字符串,序列化時通過 某種符號表示空節點(#),以 ! 表示一個結點值的結束(value!)。二叉樹的反序列化是指:根據某種遍歷順序得到的序列化字符串結果str,重構二叉樹。

序列化二叉樹

public class Solution {
    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();
    }
    int index = -1;
    TreeNode Deserialize(String str) {
        String []res = str.split(",");
        TreeNode root = null;
        if(!res[++ index].equals("#")) {
            root = new TreeNode(Integer.parseInt(res[index]));
            root.left = Deserialize(str);
            root.right = Deserialize(str);
        }
        return root;
    }
}

62 二叉搜索樹的第k個結點

給定一棵二叉搜索樹,請找出其中的第k小的結點。例如, (5,3,7,2,4,6,8) 中,按結點數值大小順序第三小結點的值爲4。

二叉搜索樹的第k個結點

public class Solution {
    TreeNode KthNode(TreeNode root, int k)
    {
        if(root == null || k == 0)return null;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode node = root;
        int count = 0;
        while(!stack.isEmpty() || node != null) {
            while(node != null) {
                stack.push(node);
                node = node.left;
            }
            node = stack.pop();
            if(++ count == k)return node;
            node = node.right;
        }
        return null;
    }
}

63 數據流中的中位數

如何得到一個數據流中的中位數?如果從數據流中讀出奇數個數值,那麼中位數就是所有數值排序之後位於中間的數值。如果從數據流中讀出偶數個數值,那麼中位數就是所有數值排序之後中間兩個數的平均值。我們使用Insert()方法讀取數據流,使用GetMedian()方法獲取當前讀取數據的中位數。

數據流中的中位數

public class Solution {
    PriorityQueue<Integer> min = new PriorityQueue<>();
    PriorityQueue<Integer> max = new PriorityQueue<>(new Comparator<Integer>(){
        @Override
        public int compare(Integer o1, Integer o2){
            return o2 - o1;
        }
    });
    public void Insert(Integer num) {
        if(max.isEmpty() || num <= max.peek())
            max.add(num);
        else min.add(num);
        
        if(max.size() == min.size() + 2)
            min.add(max.poll());
        if(max.size() + 1 == min.size())
            max.add(min.poll());
    }

    public Double GetMedian() {
        return max.size() == min.size() ? (max.peek() + min.peek()) / 2.0 : max.peek() * 1.0;
    }
}

64 滑動窗口的最大值

給定一個數組和滑動窗口的大小,找出所有滑動窗口裏數值的最大值。例如,如果輸入數組{2,3,4,2,6,2,5,1}及滑動窗口的大小3,那麼一共存在6個滑動窗口,他們的最大值分別爲{4,4,6,6,6,5}; 針對數組{2,3,4,2,6,2,5,1}的滑動窗口有以下6個: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

滑動窗口的最大值

public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size)
    {
        ArrayList<Integer> res = new ArrayList<>();
        if(num == null || num.length == 0 || size == 0)return res;
        LinkedList<Integer> dq = new LinkedList<>();
        
        for(int i = 0; i < num.length; i ++) {
          	//確保隊列存的是最大值和第二大值
            while(!dq.isEmpty() && num[dq.peekLast()] <= num[i])
                dq.pollLast();
            
            dq.addLast(i);
            //刪除過期最大值
            if(dq.peekFirst() + size == i)
                dq.pollFirst();
            //當窗口達到大小才添加最大值
            if(i + 1 >= size) {
                res.add(num[dq.peekFirst()]);
            }
        }
        return res;
    }
}

65 矩陣中的路徑

請設計一個函數,用來判斷在一個矩陣中是否存在一條包含某字符串所有字符的路徑。

矩陣中的路徑

public class Solution {
    boolean visited[];
    int rows,cols;
    
    public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
    {
        if(rows == 0 || str == null || str.length == 0)return false;
        this.rows = rows;this.cols = cols;
        visited = new boolean[rows * cols];
        
        for(int i = 0; i < rows; i ++) {
            for(int j = 0; j < cols; j ++) {
                if(isSatisfy(matrix, str, i, j, 0))return true;
            }
        }
        return false;
    }

    private boolean isSatisfy(char[] matrix, char[] str, int i, int j, int k) {
        int index = i * cols + j;
        if(i < 0 || i >= rows || j < 0 || j >= cols 
           || visited[index] || matrix[index] != str[k])
            return false;
        
        if(k == str.length - 1) return true;//出口
        visited[index] = true;//遞
        if(isSatisfy(matrix, str, i + 1, j, k + 1) ||
           isSatisfy(matrix, str, i - 1, j, k + 1) ||
           isSatisfy(matrix, str, i, j + 1, k + 1) ||
           isSatisfy(matrix, str, i, j - 1, k + 1))
            return true;
        visited[index] = false;//歸
        return false;
    }
}

66 機器人的運動範圍

地上有一個m行和n列的方格。一個機器人從座標0,0的格子開始移動,每一次只能向左,右,上,下四個方向移動一格,但是不能進入行座標和列座標的數位之和大於k的格子。 例如,當k爲18時,機器人能夠進入方格(35,37),因爲3+5+3+7 = 18。但是,它不能進入方格(35,38),因爲3+5+3+8 = 19。請問該機器人能夠達到多少個格子?

機器人的運動範圍

public class Solution {
    boolean visited[];
    int rows,cols;
    public int movingCount(int k, int rows, int cols)
    {
        if(rows == 0)return 0;
        if(k < 0)return 0;
        this.rows = rows;this.cols = cols;
        visited = new boolean[rows * cols];
        return fill(0, 0, k);
    }
    private int fill(int i, int j, int k) {
        int index = i * cols + j;
        if(i < 0 || i >= rows || j < 0 || j >= cols 
           || visited[index] || !trueSum(i, j, k))
            return 0;
        
        visited[index] = true;
        int count = fill(i + 1, j, k) +
           fill(i - 1, j, k) +
           fill(i, j + 1, k) +
           fill(i, j - 1, k) + 1;
        return count;
    }
    
    private boolean trueSum(int i, int j, int k){
        String s = "" + i + j;
        int sum = 0;
        for(char a : s.toCharArray()) {
            sum += a - '0';
        }
        return sum <= k;
    }
}

67 剪繩子

給你一根長度爲n的繩子,請把繩子剪成整數長的m段(m、n都是整數,n>1並且m>1),每段繩子的長度記爲k[0],k[1],…,k[m]。請問k[0]xk[1]x…xk[m]可能的最大乘積是多少?例如,當繩子的長度是8時,我們把它剪成長度分別爲2、3、3的三段,此時得到的最大乘積是18。

剪繩子

思路:每個繩子都可以分成兩個最優的兩段

public class Solution {
    public int cutRope(int target) {
        //如果必須切一刀的最大值
        if(target == 1)return 1;
        if(target == 2)return 1;
        if(target == 3)return 2;
        int memo[] = new int[target + 1];
        //因爲在尋找最大子問題時候,不是必須切,所以2,3的最大值不一樣
        memo[1] = 1;
        memo[2] = 2;
        memo[3] = 3;
        for(int i = 4; i <= target; i ++) {
            //i 分成兩段,所以不需要循環到i
            for(int j = 1; j <= i / 2; j ++) {
                memo[i] = Math.max(memo[i], memo[j] * memo[i - j]);
            }
        }
        return memo[target];
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章