zoukankan      html  css  js  c++  java
  • LeetCode题目解答

    LeetCode题目解答——Easy部分

    alg[Updated on 9/22/2017] 如今回头看来,里面很多做法都不是最佳的,有的从复杂度上根本就不是最优解,有的写的太啰嗦,有的则用了一些过于tricky的方法。我没有为了这个再更新,就让它们去吧。

    LeetCode最近很火,我以前不太知道有这么一个很方便练习算法的网站,直到大概数周前同事和我说起,正好我老婆要找工作,而根据同事的理论,LeetCode的题目是必须攻破的第一道关卡。我虽说又不找工作,但是纯粹拿来练手和学习,觉得很多题目都挺有趣的。现在已经做了三分之一,我会把我的解答分几次放上来。这里是第一部分,难度为easy的题目。

    我觉得做这样的题目很有帮助,但也要有正确的目的。有些题是锻炼思维的,我比较喜欢;有的题目是考察问题分析得仔细不仔细,各种corner case,我觉得没太大意思;还有一些题目则是要求具备一些算法数据结构之外的知识,比如罗马数字什么的,这样的题目就更不好了。

    (点击表格中题目的名称进入解答详情)

    Palindrome Number 28.8% Easy
    ZigZag Conversion 23.4% Easy
    Valid Sudoku 27.6% Easy
    Add Binary 25.5% Easy
    Valid Parentheses 28.2% Easy
    Valid Palindrome 22.2% Easy
    Balanced Binary Tree 32.5% Easy
    Valid Number 11.0% Easy
    Symmetric Tree 31.6% Easy
    String to Integer (atoi) 14.2% Easy
    Same Tree 41.8% Easy
    Binary Tree Level Order Traversal 30.6% Easy
    Binary Tree Level Order Traversal II 31.0% Easy
    Roman to Integer 33.9% Easy
    Reverse Integer 39.8% Easy
    Remove Nth Node From End of List 29.3% Easy
    Remove Element 33.0% Easy
    Remove Duplicates from Sorted List 34.7% Easy
    Climbing Stairs 34.0% Easy
    Remove Duplicates from Sorted Array 32.2% Easy
    Plus One 31.4% Easy
    Path Sum 30.4% Easy
    Pascal’s Triangle II 30.1% Easy
    Pascal’s Triangle 31.1% Easy
    Minimum Depth of Binary Tree 29.4% Easy
    Merge Two Sorted Lists 33.2% Easy
    Merge Sorted Array 31.8% Easy
    Maximum Depth of Binary Tree 43.8% Easy
    Longest Common Prefix 27.0% Easy
    Count and Say 26.7% Easy
    Length of Last Word 29.0% Easy
    Implement strStr() 21.9% Easy

    Palindrome Number

    【题目】Determine whether an integer is a palindrome. Do this without extra space.

    【解答】Palindrome指的是回文,而这里需要找的是回文数,指的是1、121、34543这样从左往右看和从右往左看都相等的数。先找到数字总共有几位,然后判断高位和低位是否相等,相等是回文数。getDigit方法是用来取数x的第i位的数字的,i从低到高记位,最低位为1。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class Solution {
        public boolean isPalindrome(int x) {
            if(x<0)
                return false;
     
            int quotient = x;
            int digits = 0;
            while (quotient!=0) {
                quotient /= 10;
                digits++;
            }
     
            for (int i=1; i<=digits; i++) {
                int high = digits - i + 1;
                int low = i;
     
                if (getDigit(x, high) != getDigit(x, low))
                    return false;
            }
     
            return true;
        }
     
        private int getDigit(int x, int i){
            if (i==1)
                return x%10;
     
            return (x / (int)Math.pow(10, i-1)) % 10;
        }
    }

    ZigZag Conversion

    【题目】The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)

    P   A   H   N
    A P L S I I G
    Y   I   R

    And then read line by line: "PAHNAPLSIIGYIR"

    Write the code that will take a string and make this conversion given a number of rows:

    string convert(string text, int nRows);

    convert("PAYPALISHIRING", 3) should return "PAHNAPLSIIGYIR".

    【解答】光看到这个题目我觉得没法理解这个题意啊,再多给几个nRows就好了。没办法去网上搜了一下,终于明白ZigZag是这样一种形式,比如在nRows=4的时候,它是:

    1
    2
    3
    4
    P  I  N
    A LS IG
    YA HR
    P  I

    就是说,它从左往右是由一个个可重复的矩形单元构成的,每个矩形高是nRows,宽是nRows-2,字母在每个矩形中构成的规则是,先从上到下排最左边一列(比如上面的PAYP),然后再从左下到右上排斜对角线(比如上面的PALI)。

    掌握了这样的信息以后,就好办了,建立一个二维数组map用来模拟存放这个东西,最后按行输出,跳过空字符即可(这里的逻辑还是有点绕的。当然,如果你能找到每一行关于字符在字符串中序号的通式,会简单得多):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    public class Solution {
        public String convert(String s, int nRows) {
            if (nRows == 1)
                return s;
     
            // each unit
            int amountInUnit = nRows + nRows - 2;
            int totalUnits = s.length() / amountInUnit;
            if (s.length() % amountInUnit != 0)
                totalUnits++;
     
            // each unit is a rectangle
            int rows = nRows;
            int cols = totalUnits * (nRows - 1);
            char[][] map = new char[rows][cols];
     
            int i = 0;
            while (i < s.length()) {
                char ch = s.charAt(i);
     
                // which unit, starts from 0
                int unitNumber = i / amountInUnit;
     
                // which postion in the unit, starts from 0
                int posInUnit = i % (amountInUnit);
     
                // if it's in the first column of the unit
                int x, y;
                if (posInUnit < nRows) {
                    x = posInUnit;
                    y = unitNumber * (nRows - 1);
                } else {
                    x = nRows - 1 - (posInUnit + 1 - nRows);
                    y = nRows - x - 1 + unitNumber * (nRows - 1);
                }
                map[x][y] = ch;
     
                i++;
     
            } // while
     
            // iterate and output
            StringBuilder sb = new StringBuilder();
            for (i = 0; i < rows; i++) {
                for (int j = 0; j < cols; j++) {
                    if (map[i][j] != 0)
                        sb.append(map[i][j]);
                }
            }
            return sb.toString();
        }
    }

    Valid Sudoku

    【题目】Determine if a Sudoku is valid, according to: Sudoku Puzzles – The Rules.

    The Sudoku board could be partially filled, where empty cells are filled with the character '.'.

    A partially filled sudoku which is valid.

    Note:
    A valid Sudoku board (partially filled) is not necessarily solvable. Only the filled cells need to be validated.

    【解答】我把Sudoku的规则贴在这里:

    Each row must have the numbers 1-9 occuring just once.

    Each column must have the numbers 1-9 occuring just once.

    And the numbers 1-9 must occur just once in each of the 9 sub-boxes of the grid.

    Click here for Help playing Online.

    算法的具体做法是:先检查行规则,再检查列规则,最后检查每个sub-box规则。利用Set的add方法,根据返回值判定是否在Set里面已经存在这个字符了,这样就不需要省掉了调用contains方法。

    另外,其实可以写得更简洁,用一个嵌套的双重循环就可以搞定,拿空间换时间,需要用三个二维数组分别表示行规则、列规则和sub-box规则的检查结果,而不是一个Set了,做法就是对每一个字符的坐标(i,j),分别算出在这三个数组中的位置并检进行重复性检查。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    public class Solution {
        public boolean isValidSudoku(char[][] board) {
            HashSet<Character> set = new HashSet<Character>();
     
            // check rows
            for (int i = 0; i < board.length; i++) {
                set.clear();
                for (int j = 0; j < board.length; j++) {
                    if (board[i][j] != '.' && !set.add(board[i][j]))
                        return false;
                }
            }
     
            // check columns
            for (int j = 0; j < board.length; j++) {
                set.clear();
                for (int i = 0; i < board.length; i++) {
                    if (board[i][j] != '.' && !set.add(board[i][j]))
                        return false;
                }
            }
     
            // check each sub box, there're p*q sub-boxes
            int totalBoxes = board.length / 3;
            for (int p = 0; p < totalBoxes; p++) {
                for (int q = 0; q < totalBoxes; q++) {
                    set.clear();
                    for (int i = p * 3; i < p * 3 + 3; i++) {
                        for (int j = q * 3; j < q * 3 + 3; j++) {
                            if (board[i][j] != '.' && !set.add(board[i][j]))
                                return false;
                        }
                    }
                }
            }
     
            return true;
        }
    }

    Add Binary

    【题目】Given two binary strings, return their sum (also a binary string).

    For example,
    a = "11"
    b = "1"
    Return "100".

    【解答】我是先循环a和b共同的子串,再单独处理余下的,但是写得看起来有点复杂了,其实可以用一个循环搞定(循环内部注意判断a或者b是否已经循环完成),循环次数等a和b中比较长那个的字符个数(还有一种思路是给短的那个字符串高位补零,补到两个字符串长度相等)。注意处理进位,特别是循环完成以后,如果还有进位要处理,需要在高位补1:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    public class Solution {
        public String addBinary(String a, String b) {
            int carry = 0;
            String s = "";
            int i, j;
            for (i = a.length() - 1, j = b.length() - 1; i >= 0 && j >= 0; i--, j--) {
                int sum = carry + a.charAt(i) - '0' + b.charAt(j) - '0';
                if (sum == 0) {
                    s = '0' + s;
                    carry = 0;
                } else if (sum == 1) {
                    s = '1' + s;
                    carry = 0;
                } else if (sum == 2) {
                    s = '0' + s;
                    carry = 1;
                } else {
                    s = '1' + s;
                    carry = 1;
                }
            }
     
            if (i >= 0)
                return prepend(s, a, i, carry);
            else if (j >= 0)
                return prepend(s, b, j, carry);
     
            if (carry == 1)
                return "1" + s;
     
            return s;
        }
     
        private String prepend(String s, String a, int i, int carry) {
            if (carry == 0)
                return a.substring(0, i + 1) + s;
     
            for (; i >= 0; i--) {
                if (carry == 1) {
                    if (a.charAt(i) == '1') {
                        s = '0' + s;
                        carry = 1;
                    } else {
                        s = '1' + s;
                        carry = 0;
                    }
                } else {
                    if (a.charAt(i) == '1') {
                        s = '1' + s;
                        carry = 0;
                    } else {
                        s = '0' + s;
                        carry = 0;
                    }
                }
            }
     
            if (carry == 1) {
                s = '1' + s;
            }
     
            return s;
        }
    }

    Valid Parentheses

    【题目】Given a string containing just the characters '('')''{''}''[' and ']', determine if the input string is valid.

    The brackets must close in the correct order, "()" and "()[]{}" are all valid but "(]" and "([)]" are not.

    【解答】

    用栈来记录前半个括号的情况,注意不同情况下如果栈为空的处理:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public class Solution {
        public boolean isValid(String s) {
            Stack<Character> stack = new Stack<>();
            for (int i=0; i<s.length(); i++) {
                char ch = s.charAt(i);
     
                if (ch=='(' || ch=='[' || ch=='{')
                    stack.push(ch);
     
                if (ch==')' && (stack.empty() || stack.pop()!='('))
                    return false;
     
                if (ch==']' && (stack.empty() || stack.pop()!='['))
                    return false;
     
                if (ch=='}' && (stack.empty() || stack.pop()!='{'))
                    return false;
     
            } // for
     
            if (!stack.empty())
                return false;
     
            return true;
        }
    }

    Valid Palindrome

    【题目】Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases.

    For example,
    "A man, a plan, a canal: Panama" is a palindrome.
    "race a car" is not a palindrome.

    Note:
    Have you consider that the string might be empty? This is a good question to ask during an interview.

    For the purpose of this problem, we define empty string as valid palindrome.

    【解答】注意大小写,注意数字和字母都要算有效字符。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    public class Solution {
        public boolean isPalindrome(String s) {
            int left = 0;
            int right = s.length()-1;
     
            while (left<right) {
                char l = s.charAt(left);
                char r = s.charAt(right);
     
                if ( !isValidChar(l) ) {
                    left++;
                    continue;
                }
                if ( !isValidChar(r) ) {
                    right--;
                    continue;
                }
     
                if (toLowerCase(l)!=toLowerCase(r))
                    return false;
     
                left++;
                right--;
            }
     
            return true;
        }
     
        private boolean isValidChar(char ch) {
            return (ch<='z' && ch>='a') || (ch<='Z' && ch>='A') || (ch>='0' && ch<='9');
        }
     
        private char toLowerCase(char ch) {
            if (ch>='a' && ch<='z')
                return ch;
            else
                return (char) ( ch + ('z'-'Z') );
        }
    }

    Balanced Binary Tree

    【题目】Given a binary tree, determine if it is height-balanced.

    For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.

    【解答】checkBalanced方法用来递归检查是否平衡的,返回树高度,偷了个巧,如果发现不平衡抛出TreeUnbalancedException异常:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    class TreeUnbalancedException extends RuntimeException {
        public TreeUnbalancedException(String msg) {
            super(msg);
        }
    }
     
    public class Solution {
        public boolean isBalanced(TreeNode root) {
            try {
                checkBalanced(root);
            } catch (TreeUnbalancedException e) {
                return false;
            }
            return true;
        }
     
        private int checkBalanced(TreeNode root){
            if (root==null)
                return 0;
            int left = checkBalanced(root.left);
            int right = checkBalanced(root.right);
     
            if(left-right<-1 || left-right>1)
                throw new TreeUnbalancedException( "" + (left-right) );
     
            if (left>=right)
                return left+1;
            else
                return right+1;
        }
    }

    Valid Number

    【题目】Validate if a given string is numeric.

    Some examples:
    "0" => true
    " 0.1 " => true
    "abc" => false
    "1 a" => false
    "2e10" => true

    Note: It is intended for the problem statement to be ambiguous. You should gather all requirements up front before implementing one.

    【解答】就是要考虑各种case,题意上看我以为存在任意空格都没关系,但是实际上它要求只有字符串前后端空格是可以被容忍的,另外,解答判定检测的时候没有考虑到16进制。我偷了个懒,用正则表达式,不过表达式写得也不足够优雅:

    1
    2
    3
    4
    5
    6
    7
    public class Solution {
        public boolean isNumber(String s) {
            if(null==s)
                return false;
            return s.matches("^\s*[\+|\-]{0,1}[0-9]*(([\.]{0,1}[0-9]+)|([0-9]+[\.]{0,1}))([e|E][\+|\-]{0,1}[0-9]+){0,1}\s*$");
        }
    }

    Symmetric Tree

    【题目】Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).

    For example, this binary tree is symmetric:

        1
       / 
      2   2
     /  / 
    3  4 4  3

    But the following is not:

        1
       / 
      2   2
          
       3    3

    【解答】从上至下一层一层遍历,注意处理某一层全部为空的情况,全部为空意味着循环出口。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    public class Solution {
     
        public boolean isSymmetric(TreeNode root) {
            if (root == null)
                return true;
            List<TreeNode> list = new ArrayList<TreeNode>();
            list.add(root.left);
            list.add(root.right);
     
            boolean containsData = true;
            while (containsData) { // BFS
                containsData = false;
                int left = list.size() / 2 - 1;
                int right = left + 1;
                List<TreeNode> leftList = new ArrayList<TreeNode>();
                List<TreeNode> rightList = new ArrayList<TreeNode>();
     
                while (left >= 0) {
                    TreeNode leftNode = list.get(left);
                    TreeNode rightNode = list.get(right);
     
                    if (leftNode == null && rightNode == null) {
                        left--;
                        right++;
                        continue;
                    }
     
                    containsData = true;
                    if (leftNode == null || rightNode == null
                            || leftNode.val != rightNode.val)
                        return false;
     
                    left--;
                    right++;
     
                    leftList.add(0, leftNode.right);
                    leftList.add(0, leftNode.left);
                    rightList.add(rightNode.left);
                    rightList.add(rightNode.right);
                }
     
                list.clear();
                list.addAll(leftList);
                list.addAll(rightList);
            }
            return true;
        }
    }

    String to Integer (atoi)

    【题目】Implement atoi to convert a string to an integer.

    Hint: Carefully consider all possible input cases. If you want a challenge, please do not see below and ask yourself what are the possible input cases.

    Notes: It is intended for this problem to be specified vaguely (ie, no given input specs). You are responsible to gather all the input requirements up front.

    spoilers alert… click to show requirements for atoi.

    Requirements for atoi:

    The function first discards as many whitespace characters as necessary until the first non-whitespace character is found. Then, starting from this character, takes an optional initial plus or minus sign followed by as many numerical digits as possible, and interprets them as a numerical value.

    The string can contain additional characters after those that form the integral number, which are ignored and have no effect on the behavior of this function.

    If the first sequence of non-whitespace characters in str is not a valid integral number, or if no such sequence exists because either str is empty or it contains only whitespace characters, no conversion is performed.

    If no valid conversion could be performed, a zero value is returned. If the correct value is out of the range of representable values, INT_MAX (2147483647) or INT_MIN (-2147483648) is returned.

    【解答】各种case,包括正负号、溢出、非法字符、空格等等(说实话这种corner case题我实在是不喜欢……):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    public class Solution {
        public int atoi(String str) {
            int digit = 0;
            double number = 0;
            str = str.trim();
     
            boolean signed = false;
     
            for (int i=str.length()-1; i>=0; i--) {
                char ch = str.charAt(i);
                if (ch>='0' && ch<='9') { // number
                    number += (ch-'0') * (int)Math.pow(10, digit);
                    digit++;
                } else if(ch=='-' || ch=='+') {
                    if(signed)
                        return 0;
                    signed = true;
     
                    if (ch=='-')
                        number = -number;
                } else {
                    number = 0;
                    digit = 0;
     
                    signed = false;
                }
            }
     
            if (number>Integer.MAX_VALUE)
                return Integer.MAX_VALUE;
            if (number<=Integer.MIN_VALUE)
                return Integer.MIN_VALUE;
     
            return (int)number;
        }
    }

    Same Tree

    【题目】Given two binary trees, write a function to check if they are equal or not.

    Two binary trees are considered equal if they are structurally identical and the nodes have the same value.

    【解答】没什么好说的,处理好空的情况:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class Solution {
        public boolean isSameTree(TreeNode p, TreeNode q) {
            // only one node is null
            if (p==null && q!=null)
                return false;
            if (p!=null && q==null)
                return false;
     
            // two nodes are both null
            if (p==null && q==null)
                return true;
     
            // val
            if (p.val!=q.val)
                return false;
     
            // neither node is null
            if (isSameTree(p.left, q.left) && isSameTree(p.right, q.right))
                return true;
     
            return false; //p!=q
        }
    }

    Binary Tree Level Order Traversal

    【题目】Given a binary tree, return the level order traversal of its nodes’ values. (ie, from left to right, level by level).

    For example:
    Given binary tree {3,9,20,#,#,15,7},

        3
       / 
      9  20
        /  
       15   7

    return its level order traversal as:

    [
      [3],
      [9,20],
      [15,7]
    ]

    【解答】层序遍历:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public class Solution {
        public List<List<Integer>> levelOrder(TreeNode root) {
            List<List<Integer>> list = new ArrayList<>();
            if (root==null)
                return list;
     
            List<TreeNode> level = new ArrayList<>();
            level.add(root);
     
            while (true) {
                List<TreeNode> newLevel = new ArrayList<>();
                List<Integer> item = new ArrayList<>();
                for (TreeNode node : level) {
                    item.add(node.val);
     
                    if (node.left!=null)
                        newLevel.add(node.left);
                    if (node.right!=null)
                        newLevel.add(node.right);
                }
                list.add(item);
                if (newLevel.isEmpty())
                    break;
                level = newLevel;
            } // while
     
            return list;
        }
    }

    Binary Tree Level Order Traversal II

    【题目】Given a binary tree, return the bottom-up level order traversal of its nodes’ values. (ie, from left to right, level by level from leaf to root).

    For example:
    Given binary tree {3,9,20,#,#,15,7},

        3
       / 
      9  20
        /  
       15   7

    return its bottom-up level order traversal as:

    [
      [15,7],
      [9,20],
      [3]
    ]

    【解答】和上面那道题很类似,把循环换成了递归,利用递归工作栈,让先遍历到的列表后插入:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class Solution {
        public List<List<Integer>> levelOrderBottom(TreeNode root) {
            List<List<Integer>> list = new ArrayList<>();
            if (null==root)
                return list;
     
            List<TreeNode> nodes = new ArrayList<>();
            nodes.add(root);
     
            traverseLevel(list, nodes);
     
            return list;
        }
     
        private void traverseLevel(List<List<Integer>> list, List<TreeNode> nodes) {
            List<Integer> item = new ArrayList<>();
            List<TreeNode> nextLevel = new ArrayList<>();
            for (TreeNode node : nodes) {
                item.add(node.val);
                if (node.left!=null)
                    nextLevel.add(node.left);
                if (node.right!=null)
                    nextLevel.add(node.right);
            }
            if (!nextLevel.isEmpty())
                traverseLevel(list, nextLevel);
     
            list.add(item);
        }
    }

    Roman to Integer

    【题目】Given a roman numeral, convert it to an integer.

    Input is guaranteed to be within the range from 1 to 3999.

    【解答】这道题我非常不喜欢,因为它考察本身有一个前提,就是你必须对罗马数字非常熟悉啊,罗马数字中字母的含义可以参考这个表,但是有一些规则却是必须要知道的,我知道大数的左侧表示减去,右侧表示加上,比如IV表示5-1=4,VII表示5+2=7,但是,MCMXCVI表示什么?C表示100,夹在两个M(M表示1000)之间,那么这个C应该被减去还是被加上?这一点没搞清楚以前,题目是没法做的。

    搞清楚其中的规律以后就好办了,罗马数字都有个特点,字母贡献值是正还是负,取决于它后面那个字母。即任意相邻两个字母,如果后面那个代表的数比前一个大,那么前一个表示的数最终贡献是要被减去的;反之,如果后面那个数比前一个小,那么前一个数是要被加到最终结果去的,因此MCMXCVI中的第一个C表示的含义是负100:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class Solution {
        public int romanToInt(String s) {
            Map<Character, Integer> map = new HashMap<Character, Integer>();
            map.put('I', 1);
            map.put('V', 5);
            map.put('X', 10);
            map.put('L', 50);
            map.put('C', 100);
            map.put('D', 500);
            map.put('M', 1000);
     
            int result = 0;
            for (int i = 0; i < s.length(); i++) {
                char ch = s.charAt(i);
                if (i == 0) {
                    result += map.get(ch);
                } else {
                    char prev = s.charAt(i - 1);
                    if (map.get(ch) > map.get(prev)) {
                        result += map.get(ch);
                        result -= map.get(prev) * 2;
                    } else {
                        result += map.get(ch);
                    }
                }
            }
     
            return result;
        }
    }

    Reverse Integer

    【题目】Reverse digits of an integer.

    Example1: x = 123, return 321
    Example2: x = -123, return -321

    【解答】注意符号的处理:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class Solution {
        public int reverse(int x) {
            int sign = 1;
            if(x<0){
                sign = -1;
                x = -x;
            }
     
            int reversed = 0;
            for (; x>0; x /= 10){
                reversed = reversed*10 + x%10;
            }
     
            return reversed*sign;
        }
    }

    Remove Nth Node From End of List

    【题目】Given a linked list, remove the nth node from the end of list and return its head.

    For example,

       Given linked list: 1->2->3->4->5, and n = 2.
    
       After removing the second node from the end, the linked list becomes 1->2->3->5.

    Note:
    Given n will always be valid.
    Try to do this in one pass.

    【解答】用一个栈来解决问题,注意特殊情况,即要删掉的节点是head:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class Solution {
        public ListNode removeNthFromEnd(ListNode head, int n) {
            Stack<ListNode> stack = new Stack<>();
            ListNode node = head;
            while (node!=null) {
                stack.push(node);
                node = node.next;
            }
     
            ListNode n1 = null;
            ListNode n2 = null;
            while (n>0) {
                n2 = n1;
                n1 = stack.pop();
                n--;
            }
     
            if (stack.empty())
                head = n2;
            else
                stack.peek().next = n2;
     
            return head;
        }
    }

    Remove Element

    【题目】Given an array and a value, remove all instances of that value in place and return the new length.

    The order of elements can be changed. It doesn’t matter what you leave beyond the new length.

    【解答】没啥可说的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Solution {
        public int removeElement(int[] A, int elem) {
            int pos = 0;
            for (int a : A) {
                if(a!=elem){
                    A[pos]=a;
                    pos++;
                }
            }
     
            return pos;
        }
    }

    Remove Duplicates from Sorted List

    【题目】Given a sorted linked list, delete all duplicates such that each element appear only once.

    For example,
    Given 1->1->2, return 1->2.
    Given 1->1->2->3->3, return 1->2->3.

    【解答】两个指针,一个current始终保持只遍历非重复的元素,另一个moving一直向前移动,注意处理链表末端的情况:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class Solution {
        public ListNode deleteDuplicates(ListNode head) {
            if(null==head)
                return null;
     
            ListNode current = head, moving = head.next;
            while (null!=moving) {
                if (moving.val==current.val) {
                    moving = moving.next;
                    if (moving==null)
                        current.next = null;
                }
                else {
                    if (current.next!=moving)
                        current.next = moving;
     
                    current = moving;
                    moving = moving.next;
                }
            }
     
            return head;
        }
    }

    Climbing Stairs

    【题目】You are climbing a stair case. It takes n steps to reach to the top.

    Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

    【解答】有好几种方法可以求解,我以前写文章分析过这个问题,就不展开了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class Solution {
        // k=1, f(k)=1
        // k=2, f(k)=2 [1,1] or [2]
        // k=3, f(k)=f(3)=f(2)+f(1)
        // k=n, f(n)=f(n-1)+f(n-2)
        // Fibonacci sequence
        public int climbStairs(int n) {
            if(n==0)
                return 0;
            if(n==1)
                return 1;
            if(n==2)
                return 2;
            int last = 2, beforeLast = 1;
            for (int i=3; i<=n; i++) {
                int newOne = last + beforeLast;
                beforeLast = last;
                last = newOne;
            }
     
            return last;
     
        }
    }

    Remove Duplicates from Sorted Array

    【题目】Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length.

    Do not allocate extra space for another array, you must do this in place with constant memory.

    For example,
    Given input array A = [1,1,2],

    Your function should return length = 2, and A is now [1,2].

    【解答】双指针:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class Solution {
        public int removeDuplicates(int[] A) {
            if (A.length<=1)
                return A.length;
     
            int i=0;
            int j=1;
            while (j<A.length) {
                if (A[j]==A[i]) {
                    j++;
                }
                else {
                    i++;
                    A[i] = A[j];
                    j++;
                }
            }
            return i+1;
        }
    }

    Plus One

    【题目】Given a non-negative number represented as an array of digits, plus one to the number.

    The digits are stored such that the most significant digit is at the head of the list.

    【解答】在从低位到高位循环的时候,不需要单独设置一个布尔变量carry表示是否有进位,因为逢9就要进位,而非9的情况,这个方法直接可以返回了。同样,在循环结束以后也不需要判断数组最高位是否为0,能走完循环的,数组最高位肯定为0,数组需要扩张一位:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class Solution {
        public int[] plusOne(int[] digits) {
            for (int i=digits.length-1; i>=0; i--) {
     
                if (digits[i]==9) {
                    digits[i]=0;
                    continue;
                } else {
                    digits[i] += 1;
                    return digits;
                }
            }
     
            // digits[0]==0
            int[] newDigits = new int[digits.length+1];
     
            newDigits[0] = 1;
            System.arraycopy(digits, 0, newDigits, 1, digits.length);
     
            return newDigits;
        }
    }

    Path Sum

    【题目】Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.

    For example:
    Given the below binary tree and sum = 22,

                  5
                 / 
                4   8
               /   / 
              11  13  4
             /        
            7    2      1

    return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22.

    【解答】没什么可说的,见代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public class Solution {
        public boolean hasPathSum(TreeNode root, int sum) {
            if (null==root)
                return false;
     
            List<Integer> list = calculate(root, sum);
            return list.contains(sum);
        }
     
        private List<Integer> calculate(TreeNode root, int sum) {
            List<Integer> list = new ArrayList<Integer>();
            if (root.left!=null) {
                for (int num : calculate(root.left, sum)){
                    num += root.val;
                    list.add(num);
                }
            }
            if (root.right!=null) {
                for (int num : calculate(root.right, sum)){
                    num += root.val;
                    list.add(num);
                }
            }
            if (root.left==null&&root.right==null){
                list.add(root.val);
            }
            return list;
        }
    }

    Pascal’s Triangle II

    【题目】Given an index k, return the kth row of the Pascal’s triangle.

    For example, given k = 3,
    Return [1,3,3,1].

    Note:
    Could you optimize your algorithm to use only O(k) extra space?

    【解答】有了Pascal’s Triangle,这道题就很好解了,但是注意题目给的example,它是从第0行开始计数的,而不是第1行:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    public class Solution {
        public List<Integer> getRow(int rowIndex) {
            List<Integer> list = new ArrayList<>();
            list.add(1);
     
            if (rowIndex==0)
                return list;
     
            for (int i=1; i<=rowIndex; i++) {
                List<Integer> newList = new ArrayList<>();
                newList.add(1);
                int prev=0;
                for (int num : list) {
                    if (prev==0){
                        prev = num;
                        continue;
                    }
                    newList.add(prev+num);
                    prev = num;
                }
                newList.add(1);
                list = newList;
            }
     
            return list;
        }
    }

    Pascal’s Triangle

    【题目】Given numRows, generate the first numRows of Pascal’s triangle.

    For example, given numRows = 5,
    Return

    [
         [1],
        [1,1],
       [1,2,1],
      [1,3,3,1],
     [1,4,6,4,1]
    ]

    【解答】原来这个Pascal三角就是杨辉三角,如果发现其中的规律就很好解了。可以寻找每一行对于行号n的关系式,也可以寻找任一行对于它上一行的递推式。还不清楚的,可以去看看杨辉三角的维基百科,特别是这个:

    PascalTriangleAnimated

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public class Solution {
        public List<List<Integer>> generate(int numRows) {
            // 0
            List<List<Integer>> list = new ArrayList<>();
            if (numRows==0)
                return list;
     
            // 1
            List<Integer> first = new ArrayList<>();
            first.add(1);
            list.add(first);
            if (numRows==1)
                return list;
     
            // >1
            for (int i=2; i<=numRows; i++) {
                List<Integer> latest = list.get(list.size()-1);
                List<Integer> item = new ArrayList<>();
                item.add(1);
                int prev = 0;
                for (int num : latest) {
                    if (prev==0) {
                        prev = num;
                        continue;
                    }
     
                    item.add(prev + num);
                    prev = num;
                }
                item.add(1);
                list.add(item);
            }
            return list;
        }
    }

    Minimum Depth of Binary Tree

    【题目】Given a binary tree, find its minimum depth.

    The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.

    【解答】广度优先遍历。注意的是题目对于根节点为空的情况认为深度为0,根节点的情况深度为1。这其实是不正确的定义——参见树的维基百科,空树的深度应该是-1,而根节点的深度为0:

    The height of a node is the length of the longest downward path to a leaf from that node. The height of the root is the height of the tree. The depth of a node is the length of the path to its root (i.e., its root path). This is commonly needed in the manipulation of the various self-balancing trees, AVL Trees in particular. The root node has depth zero, leaf nodes have height zero, and a tree with only a single node (hence both a root and leaf) has depth and height zero. Conventionally, an empty tree (tree with no nodes, if such are allowed) has depth and height -1.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public class Solution {
        public int minDepth(TreeNode root) {
            if (null==root)
                return 0;
     
            List<TreeNode> list = new ArrayList<>();
            list.add(root);
     
            int depth = 1;
            while (true) {
                List<TreeNode> newList = new ArrayList<>();
                for (TreeNode node : list) {
                    if (node.left==null && node.right==null)
                        return depth;
     
                    if (null!=node.left)
                        newList.add(node.left);
                    if (null!=node.right)
                        newList.add(node.right);
                }
     
                list = newList;
                depth++;
            }
        }
    }

    Merge Two Sorted Lists

    【题目】Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.

    【解答】注意头和尾的处理:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    public class Solution {
        public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
            if (null==l1)
                return l2;
            if (null==l2)
                return l1;
     
            ListNode head, tail;
            if(l1.val<=l2.val) {
                head = tail = l1;
                l1 = l1.next;
            }
            else {
                head = tail = l2;
                l2 = l2.next;
            }
     
            while (l1!=null&&l2!=null) {
                if(l1.val<=l2.val){
                    tail.next = l1;
                    tail = l1;
                    l1 = l1.next;
                }
                else{
                    tail.next = l2;
                    tail = l2;
                    l2 = l2.next;
                }
            }
     
            if (l1!=null) {
                tail.next = l1;
            }
            if (l2!=null){
                tail.next = l2;
            }
     
            return head;
        }
    }

    Merge Sorted Array

    【题目】Given two sorted integer arrays A and B, merge B into A as one sorted array.

    Note:
    You may assume that A has enough space (size that is greater or equal to m + n) to hold additional elements from B. The number of elements initialized in A and B are m andn respectively.

    【解答】

    这道题如果从前往后把B归并到A的话,每次插入操作都会导致A中插入的数后面的所有数后移,但是如果是从后往前归并的话,这个低效的行为就巧妙地避免了,而且,归并完毕后,如果A中还有剩余元素,它们天然地躺在最小端,什么额外的事情都不需要做:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public class Solution {
        public void merge(int A[], int m, int B[], int n) {
            // for write to A
            int cur = m+n-1;
     
            // for A
            int i = m-1;
            // for B
            int j =n-1;
     
            for(; i>=0 && j>=0; cur--)
            {
                if(A[i] >= B[j])
                {
                    A[cur] = A[i];
                    i--;
                }
                else
                {
                    A[cur] = B[j];
                    j--;
                }
            }
     
            while(j >=0) // don't need to do anything if i>=0
            {
                A[cur] = B[j];
                cur--;
                j--;
            }
        }
    }

    Maximum Depth of Binary Tree

    【题目】Given a binary tree, find its maximum depth.

    The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.

    【解答】还是和前面求最小深度的题目提到的一样,在这里根节点被认为深度为1,空树被认为深度为0:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class Solution {
        int max = 0;
        public int maxDepth(TreeNode root) {
            if (null==root)
                return 0;
     
            traverse(root, 1);
            return max;
        }
     
        private void traverse(TreeNode root, int depth) {
            if (depth >= max)
                max = depth;
            if (null != root.left)
                traverse(root.left, depth+1);
            if (null != root.right)
                traverse(root.right, depth+1);
        }
    }

    Longest Common Prefix

    【题目】Write a function to find the longest common prefix string amongst an array of strings.

    【解答】注意入参空数组:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class Solution {
        public String longestCommonPrefix(String[] strs) {
            if (strs.length==0)
                return "";
     
            int i=0;
            StringBuilder sb = new StringBuilder();
            while (true) {
                char ch = 0;
                for (String s : strs) {
                    if (i==s.length())
                        return sb.toString();
     
                    if (ch==0 || ch==s.charAt(i))
                        ch = s.charAt(i);
                    else
                        return sb.toString();
                }
                sb.append(ch);
                i++;
            }
        }
    }

    Count and Say

    【题目】The count-and-say sequence is the sequence of integers beginning as follows:
    1, 11, 21, 1211, 111221, ...

    1 is read off as "one 1" or 11.
    11 is read off as "two 1s" or 21.
    21 is read off as "one 2, then one 1" or 1211.

    Given an integer n, generate the nth sequence.

    Note: The sequence of integers will be represented as a string.

    【解答】唯一需要注意的就是对于特殊值n=1的处理:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public class Solution {
        public String countAndSay(int n) {
            String s = "1";
            if (n == 1)
                return s;
     
            for (int i = 2; i <= n; i++) {
                char lastChar = 0;
                int lastAmount = 0;
     
                StringBuilder sb = new StringBuilder();
                for (int j = 0; j < s.length(); j++) {
                    char ch = s.charAt(j);
                    if (0 != lastAmount && ch == lastChar)
                        lastAmount++;
                    else {
                        if (0 != lastAmount)
                            sb.append(lastAmount).append(lastChar);
     
                        lastChar = ch;
                        lastAmount = 1;
                    }
                }
                sb.append(lastAmount).append(lastChar);
                s = sb.toString();
            }
            return s;
        }
    }

    Length of Last Word

    【题目】Given a string s consists of upper/lower-case alphabets and empty space characters ' ', return the length of last word in the string.

    If the last word does not exist, return 0.

    Note: A word is defined as a character sequence consists of non-space characters only.

    For example,
    Given s = "Hello World",
    return 5.

    【解答】留意尾部有空格的情况:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Solution {
        public int lengthOfLastWord(String s) {
            if (null==s || "".equals(s.trim()))
                return 0;
     
            String[] tokens = s.trim().split(" ");
     
            return tokens[tokens.length-1].length();
        }
    }

    Implement strStr()

    【题目】Implement strStr().

    Returns the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.

    【解答】其实最好的解法应该是KMP算法,我的实现只是挨个遍历:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class Solution {
        public int strStr(String haystack, String needle) {
            if(needle.length() == 0)
                return 0;
     
            for(int i = 0; i <= haystack.length() - needle.length(); i++) {
                int n = 0;
                while(n < needle.length() && haystack.charAt(i+n) == needle.charAt(n))
                    n++;
                if(n == needle.length())
                    return i;
            }
     
            return -1;
        }
    }

    文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接《四火的唠叨》

    LeetCode题目解答——Medium部分(上)

    [Updated on 9/22/2017] 如今回头看来,里面很多做法都不是最佳的,有的从复杂度上根本就不是最优解,有的写的太啰嗦,有的则用了一些过于tricky的方法。我没有为了这个再更新,就让它们去吧。

    以下是LeetCode题目中Medium部分的上半部分,点击表格中的名称进入题目和解答。我计划把LeetCode我的解答分成四个部分发上来,这是第二部分。做这些题目收获还是挺大的。

    Divide Two Integers 16.6% Medium
    3Sum 16.7% Medium
    Evaluate Reverse Polish Notation 19.9% Medium
    Find Minimum in Rotated Sorted Array 31.7% Medium
    Word Search 19.8% Medium
    Word Ladder 18.4% Medium
    Flatten Binary Tree to Linked List 28.2% Medium
    Gas Station 25.9% Medium
    Generate Parentheses 31.4% Medium
    Gray Code 32.1% Medium
    Word Break 21.3% Medium
    Validate Binary Search Tree 25.9% Medium
    Insertion Sort List 25.3% Medium
    Integer to Roman 33.8% Medium
    4Sum 21.4% Medium
    Jump Game 27.2% Medium
    Add Two Numbers 22.9% Medium
    Anagrams 23.9% Medium
    Decode Ways 16.2% Medium
    Letter Combinations of a Phone Number 26.4% Medium
    Linked List Cycle 35.7% Medium
    Linked List Cycle II 30.8% Medium
    Best Time to Buy and Sell Stock 31.2% Medium
    Unique Paths II 27.9% Medium
    Longest Palindromic Substring 20.6% Medium
    Longest Substring Without Repeating Characters 22.2% Medium
    Unique Paths 31.7% Medium
    Unique Binary Search Trees II 27.3% Medium
    Unique Binary Search Trees 36.5% Medium
    Two Sum 18.4% Medium
    Convert Sorted List to Binary Search Tree 27.3% Medium
    Maximum Product Subarray 15.9% Medium
    Maximum Subarray 34.0% Medium
    Triangle 26.6% Medium
    Best Time to Buy and Sell Stock II 36.6% Medium
    Swap Nodes in Pairs 32.4% Medium
    Convert Sorted Array to Binary Search Tree 32.9% Medium
    Container With Most Water 31.3% Medium
    Minimum Path Sum 31.0% Medium
    Surrounded Regions 14.2% Medium

    Divide Two Integers

    【题目】Divide two integers without using multiplication, division and mod operator.

    【解答】不能用乘除和取模,但是还是可以用加减和左移的嘛,第一遍循环先找到最高位,看到底要左移多少;第二遍循环从最高位开始,不断除以2的幂。

    有一些需要特殊考虑的case,比如说:

    • 符号问题;
    • 整型溢出问题,使用Math.abs方法要当心,如果数值是-2147483648的话,由于溢出的关系,取绝对值的结果还是它自己,所以在取绝对值之前先给转成了long;
    • 2的x次方,就左移x位好了,但是当x=0需要特殊处理。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    public class Solution {
        public int divide(int dividend, int divisor) {
            boolean sign = (dividend < 0 && divisor < 0)
                    || (dividend >= 0 && divisor >= 0);
            long dividendL = Math.abs((long)dividend);
            long divisorL = Math.abs((long)divisor);
     
            int res = 0;
            int move = 0;
            while (true) {
                if ((divisorL << move) > dividendL) { // found the highest digit
                    break;
                }
     
                move++;
     
            } // while
     
            long temp = dividendL;
            for (int i = move - 1; i >= 0; i--) {
                long newDivisor = divisorL << i;
                if (temp - newDivisor < 0)
                    continue;
     
                temp -= newDivisor;
                if (i == 0)
                    res += 1;
                else
                    res += 2 << (i - 1);
            }
     
            if (!sign)
                res = -res;
            return res;
        }
    }

    3Sum

    【题目】Given an array S of n integers, are there elements abc in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.

    Note:

    • Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
    • The solution set must not contain duplicate triplets.
        For example, given array S = {-1 0 1 2 -1 -4},
    
        A solution set is:
        (-1, 0, 1)
        (-1, -1, 2)

    【解答】首先要排序,排序之后就可以利用数规律的排列简化计算。由于要求最终结果是0,最开始我的办法是分为0、负数和正数来讨论,结果写出了答案,却很复杂。但是实际上并不需要考虑0这个特殊值,其实给定任意一个数,都有类似的做法:使用双指针,一个left,一个right,前者从最左侧开始,只能向右移动;后者相反,从最右侧开始,只能向左移动。如果sum大于目标值,那么right左移;如果sum小于目标值,那么left右移。

    从0推广到任意数是如此计算,从三个数推广到四个数还是如此计算。

    另外,需要注意去重,可以利用一个HashSet来去重,也可以在每次指针移动之后比较最新值和前一个值,如果一样就跳过:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    public class Solution {
        public List<List<Integer>> threeSum(int[] num) {
            List<List<Integer>> list = new ArrayList<>();
            if (num.length<3)
                return list;
     
            Arrays.sort(num);
     
            for (int i=0; i<num.length-2; i++) {
                if(i!=0 && num[i]==num[i-1])
                    continue;
     
                int left = i+1;
                int right = num.length-1;
                while (left<right) {
                    if (left>i+1 && num[left]==num[left-1]) {
                        left++;
                        continue;
                    }
                    if (right<num.length-2 && num[right]==num[right+1]) {
                        right--;
                        continue;
                    }
     
                    int sum = num[i] + num[left] + num[right];
                    if (sum==0) {
                        List<Integer> item = new ArrayList<>();
                        item.add(num[i]);
                        item.add(num[left]);
                        item.add(num[right]);
                        list.add(item);
     
                        left++;
                        right--;
                    } else if (sum>0) {
                        right--;
                    } else {
                        left++;
                    }
                } // while
            } // for
     
            return list;
        }
    }

    Evaluate Reverse Polish Notation

    【题目】Evaluate the value of an arithmetic expression in Reverse Polish Notation.

    Valid operators are +-*/. Each operand may be an integer or another expression.

    Some examples:

      ["2", "1", "+", "3", "*"] -> ((2 + 1) * 3) -> 9
      ["4", "13", "5", "/", "+"] -> (4 + (13 / 5)) -> 6

    【解答】就是逆波兰式求解,利用一个栈可以搞定。注意从栈里面pop出来的时候,先出来那个是运算符右边的数,再出来的才是左边的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class Solution {
        public int evalRPN(String[] tokens) {
            Stack<Integer> stack = new Stack<>();
            for (String token : tokens) {
                if (token.matches("[+-]{0,1}\d+")) {
                    stack.push(Integer.parseInt(token));
                } else {
                    int right = stack.pop();
                    int left = stack.pop();
     
                    int result;
                    if ("+".equals(token))
                        result = left + right;
                    else if ("-".equals(token))
                        result = left - right;
                    else if ("*".equals(token))
                        result = left * right;
                    else
                        result = left / right;
     
                    stack.push(result);
                }
            }
     
            if (stack.isEmpty())
                return 0;
            return stack.pop();
        }
     
    }

    Find Minimum in Rotated Sorted Array

    【题目】Suppose a sorted array is rotated at some pivot unknown to you beforehand.

    (i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).

    Find the minimum element.

    You may assume no duplicate exists in the array.

    【解答】就是挨个遍历,复杂度也只有n阶,但是还是可以利用类似二分法来找到最小值。通过二分的时候,最左值与中间值大小的比较,有这么两种情况:

    1. 最左值≤中间值,这意味着要么最左值就是最小值,要么最小值在右半边;
    2. 最左值>中间值,这意味着最大最小值都在左半边。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    public class Solution {
        public int findMin(int[] num) {
            if (num.length==0)
                return num[0];
     
            int left=0;
            int right=num.length-1;
            int min = Integer.MAX_VALUE;
     
            while (left<right) {
                int mid = (left+right)/2;
                if (left==mid)
                    break;
     
                // 3 4 5 6 2, left part is equal or ascending, so for this part num[left] is the smallest
                if(num[left]<=num[mid]) {
                    if (num[left]<min)
                        min = num[left];
                    left= mid+1;
                }
                // 5 6 1 2 3, both min and max value are in left part
                else {
                    if (num[mid]<min)
                        min = num[mid];
                    right = mid-1;
                }
            }
     
            if (num[right]<min)
                min = num[right];
            if (num[left]<min)
                min = num[left];
     
            return min;
        }
    }

    Word Search

    【题目】Given a 2D board and a word, find if the word exists in the grid.

    The word can be constructed from letters of sequentially adjacent cell, where “adjacent” cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.

    For example,
    Given board =

    [
      ["ABCE"],
      ["SFCS"],
      ["ADEE"]
    ]

    word = "ABCCED", -> returns true,
    word = "SEE", -> returns true,
    word = "ABCB", -> returns false.

    【解答】这个题目很像贪吃蛇,找一条路径,并且不能碰到自己,类似这种找路径的问题,包括迷宫之类的,都可以考虑回溯法解决。回溯法基本上就这几个步骤:

    1. 找出发点;
    2. 按约定选一个方向前进,当前位置入栈;
    3. 没有路了就后退,出栈;
    4. 按约定寻找下一个方向前进。

    如果是递归的回溯法,这个栈可以免去,递归工作栈来代替。这道题使用一个二维数组used来存放位置是否走过的信息,参数from的取值1、2、3、4分别表示从上方、左边、下方、右边来到当前位置,这样在寻找下一个位置的时候,过来的路线直接跳过。下面的解答需要注意的地方是,在每个位置,递归调用exist方法前,要把下一个位置的状态(现场)保存下来,等到调用完毕之后,要恢复现场。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    public class Solution {
        public boolean exist(char[][] board, String word) {
            if (word.length() == 0)
                return true;
            for (int i = 0; i < board.length; i++)
                for (int j = 0; j < board[0].length; j++)
                    if (word.charAt(0) == board[i][j]
                            && exist(board, word, 0, i, j,
                                    new boolean[board.length][board[0].length], 0))
                        return true;
     
            return false;
        }
     
        private boolean exist(char[][] board, String word, int pos, int i, int j,
                boolean[][] used, int from) {
     
            if (pos == word.length())
                return true;
            if (used[i][j] || board[i][j] != word.charAt(pos))
                return false;
     
            // for special case, {{'a'}}, "a"
            if (pos == word.length() - 1)
                return true;
     
            // mark it used
            used[i][j] = true;
            boolean temp;
     
            // up
            if (i > 0 && from != 1) {
                temp = used[i - 1][j];
                if (exist(board, word, pos + 1, i - 1, j, used, 3))
                    return true;
                used[i - 1][j] = temp; // roll back
            }
     
            // left
            if (j > 0 && from != 2) {
                temp = used[i][j - 1];
                if (exist(board, word, pos + 1, i, j - 1, used, 4))
                    return true;
                used[i][j - 1] = temp; // roll back
            }
     
            // down
            if (i < board.length - 1 && from != 3) {
                temp = used[i + 1][j];
                if (exist(board, word, pos + 1, i + 1, j, used, 1))
                    return true;
                used[i + 1][j] = temp; // roll back
            }
     
            // right
            if (j < board[0].length - 1 && from != 4) {
                temp = used[i][j + 1];
                if (exist(board, word, pos + 1, i, j + 1, used, 2))
                    return true;
                used[i][j + 1] = temp; // roll back
            }
     
            return false;
        }
    }

    Word Ladder

    【题目】Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that:

    1. Only one letter can be changed at a time
    2. Each intermediate word must exist in the dictionary

    For example,

    Given:
    start = "hit"
    end = "cog"
    dict = ["hot","dot","dog","lot","log"]

    As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
    return its length 5.

    Note:

    • Return 0 if there is no such transformation sequence.
    • All words have the same length.
    • All words contain only lowercase alphabetic characters.

    【解答】这个题目看起来是要找一条path,使得可以从start走到end,再看发现其实是一个关于广度优先搜索的题目:

    1. 这棵树从start开始,每一个在词典里面的词如果变化一个字母,派生成的那个新词还在词典里面,那就可以看做那个新词就是老词的一个子节点;
    2. 这棵树一层一层往下生长,每生长一层,距离就要+1;
    3. 每找到一个在词典里的词,就要把它从词典里面删掉,这样可以保证词典里面每一个词出现在这棵树上的时候,距离都是尽可能小的;
    4. 直到发现end在新长出来的叶子节点里面,就算找到了;
    5. 如果词典里面都没词了还没找到,那就说明不存在这样的路径。

    具体代码需要注意的地方包括,如果对一个普通的HashSet/HashMap元素遍历的期间,对其add一个新元素,会抛出java.util.ConcurrentModificationException,所以这个问题需要避免:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    class Node {
        public String word;
        public int distance;
     
        public Node(String word, int distance) {
            this.word = word;
            this.distance = distance;
        }
    }
     
    public class Solution {
     
        public int ladderLength(String start, String end, Set<String> dict) {
            if (start.equals(end))
                return 1;
     
            Set<Node> set = new HashSet<Node>();
            set.add(new Node(start, 1));
     
            while (!dict.isEmpty()) {
                // use newSet to avoid java.util.ConcurrentModificationException
                Set<Node> newSet = new HashSet<Node>();
                for (Node node : set) {
                    for (int i = 0; i < node.word.length(); i++) {
                        for (char ch = 'a'; ch <= 'z'; ch++) {
                            char[] arr = node.word.toCharArray();
                            arr[i] = ch;
                            String newWord = new String(arr);
                            if (end.equals(newWord))
                                return node.distance + 1;
     
                            if (dict.contains(newWord)) {
                                dict.remove(newWord);
                                newSet.add(new Node(newWord, node.distance + 1));
                            }
                        } // for: 'a'~'z'
                    } // for: word[i]
                } // for: nodes
     
                if (newSet.isEmpty())
                    return 0;
                set = newSet;
     
            } // while
     
            return 0;
        }
    }

    另外,我还找到了一个更快一些的办法,是从start和end两边搜索,因为一棵树越接近根的地方越小,在生长的时候每一层的叶子节点的循环次数就越小,具体分析在这篇文章里(下图来自该文章):

    double-BFS 

    Flatten Binary Tree to Linked List

    【题目】Given a binary tree, flatten it to a linked list in-place.

    For example,
    Given

             1
            / 
           2   5
          /    
         3   4   6

    The flattened tree should look like:

       1
        
         2
          
           3
            
             4
              
               5
                
                 6

    【解答】观察一下规律,其实就是先序遍历:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class Solution {
        public void flatten(TreeNode root) {
            if (root != null)
                traverse(root);
        }
     
        private TreeNode traverse(TreeNode root) {
            TreeNode right = root.right;
            TreeNode left = root.left;
            TreeNode tail = root;
     
            root.left = null;
            root.right = null;
     
            if (null != left) {
                root.right = left;
                tail = traverse(left);
            }
     
            if (null != right) {
                tail.right = right;
                tail = traverse(root.right);
            }
     
            tail.left = null;
            tail.right = null;
     
            return tail;
        }
    }

    Gas Station

    【题目】There are N gas stations along a circular route, where the amount of gas at station i is gas[i].

    You have a car with an unlimited gas tank and it costs cost[i] of gas to travel from station i to its next station (i+1). You begin the journey with an empty tank at one of the gas stations.

    Return the starting gas station’s index if you can travel around the circuit once, otherwise return -1.

    Note:
    The solution is guaranteed to be unique.

    【解答】这道题我一开始看到,很快就写出来了,提交了,也提示Accepted,但是复杂度是n平方的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public class Solution {
        public int canCompleteCircuit(int[] gas, int[] cost) {
     
            for (int start=0; start<gas.length; start++) {
                int g = 0;
                int i = start;
                boolean flag = false;
     
                do {
                    if (flag && i==start)
                        return start;
                    flag = true;
     
                    g += gas[i];
                    g -= cost[i];
     
                    if (i==gas.length-1)
                        i=0;
                    else
                        i++;
                } while (g>=0);
            }
     
            return -1;
        }
    }

    但是,我老婆看了我的答案说,人家可是用O(n)来做的。

    那,这还能再优化么?

    回过头来看一下问题,冠冕堂皇地搞了一个gas数组,又搞了一个cost数组,其实,对于环上的每一个点i而言,我关心的只有gas[i]-cost[i]而已,我把它定义为diff[i]。

    1. 显而易见的一点是,假设sum(p,q)表示从diff[p]到diff[q]这些项求和,那么如果p为0,q为n-1,即所有的数之和如果小于零的话,sum(0, n-1) = ∑diff[i]<0(i∈[0, n-1]),根本是无法做到有一点让轿车跑一圈的。

    那么对于这个和大于0的情况,如何找到这一个出发点呢?

    2. 这个出发点假设是k的话,我可以说,那么对于k前面的任意一点p,sum(p,k-1)必须满足sum(p,k-1)<0,否则的话,一定可以从[p,k-1]中找到一点,作为新的出发点,这个出发点可以使得也一样可以成功绕一圈,而注意到题中提示,“The solution is guaranteed to be unique.”,既然只有一个解,那么这个点就只能不存在了。所以可以得到:对于k前面的任意一点p,sum(p,k-1)必须满足sum(p,k-1)<0,现在取p=0,即所有的sum(0,k-1)全部小于零。

    3. 再来看k后面的情况,对于k后面的任意一点q,一定有sum(k+1,q)≥0,否则的话,从k+1到q就走不了了。

    有了上面1~3点的分析,有点绕,但是想清楚了代码就很简单了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class Solution {
        public int canCompleteCircuit(int[] gas, int[] cost) {
            int total = 0;
            int sum = 0;
            int start = 0;
            for (int i=0; i<gas.length; i++) {
                int diff = gas[i] - cost[i];
                total += diff;
                sum += diff;
     
                if (sum<0) {
                    sum = 0;
                    start = i+1;
                }
            }
     
            if (total<0)
                return -1;
            return start;
        }
    }

    Generate Parentheses

    【题目】Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

    For example, given n = 3, a solution set is:

    "((()))", "(()())", "(())()", "()(())", "()()()"

    【解答】有几种方法可以解这道题,我觉得最容易理解的方式是这样,以自然的方式,从左到右写出这一堆括号来,每次下笔都有两种选择,要么写左括号,要么写右括号,具体说,每一次下笔都必须满足这样的条件:

    • 写过的左括号次数必须小于n,那就可以写左括号;
    • 写过的右括号次数必须小于n,并且也小于左括号次数,那就可以写右括号。

    写成代码就是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class Solution {
        public List<String> generateParenthesis(int n) {
            List<String> list = new ArrayList<>();
            append(n, 0, 0, "", list);
            return list;
        }
     
        private void append(int n, int left, int right, String s, List<String> list) {
            if (left==n && right==n) {
                list.add(s);
                return;
            }
     
            if (left<n) {
                append(n, left+1, right, s+'(', list);
            }
     
            if (right<n && left>right) {
                append(n, left, right+1, s+')', list);
            }
        }
    }

    当然,也有其它的解法,比如说,考虑递推式,在n=k的时候,我有一个括号的序列合集,现在当n=k+1的时候,我要给这堆括号的序列合集里面加上一组新的括号,可以用动态规划来解,但是这样的解法要复杂一些。

    Gray Code

    【题目】The gray code is a binary numeral system where two successive values differ in only one bit.

    Given a non-negative integer n representing the total number of bits in the code, print the sequence of gray code. A gray code sequence must begin with 0.

    For example, given n = 2, return [0,1,3,2]. Its gray code sequence is:

    00 - 0
    01 - 1
    11 - 3
    10 - 2

    Note:
    For a given n, a gray code sequence is not uniquely defined.

    For example, [0,2,3,1] is also a valid gray code sequence according to the above definition.

    For now, the judge is able to judge based on one instance of gray code sequence. Sorry about that.

    【解答】格雷码,先去读了读维基百科,在注意到它的生成规则具备镜像特性以后,问题就好办了:

    GrayCode

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    public class Solution {
        public List<Integer> grayCode(int n) {
            if (n==0){
                List<Integer> list = new ArrayList<Integer>();
                list.add(0);
                return list;
            }
     
            List<String> list = strGrayCode(n);
            return convert(list);
        }
     
        private List<Integer> convert(List<String> list){
            List<Integer> ret = new ArrayList<Integer>();
            for (String str : list) {
                int sum = 0;
                for (int i=0; i<str.length(); i++) {
                    char bit = str.charAt(i);
                    if (bit=='1')
                        sum += 1<<(str.length()-1-i); //Math.pow(2, x);
                }
                ret.add(sum);
            }
     
            return ret;
        }
     
        private List<String> strGrayCode(int n) {
            if (n==1) {
                List<String> list = new ArrayList<String>();
                list.add("0");
                list.add("1");
                return list;
            }
     
            List<String> list = strGrayCode(n-1);
            List<String> newList = new ArrayList<String>();
            for (int i=0; i<list.size() ;i++) {
                newList.add("0"+list.get(i));
            }
            for (int i=list.size()-1; i>=0 ;i--) {
                newList.add("1"+list.get(i));
            }
     
            return newList;
        }
    }

    Word Break

    【题目】Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

    For example, given
    s = "leetcode",
    dict = ["leet", "code"].

    Return true because "leetcode" can be segmented as "leet code".

    【解答】动态规划,对于字符串[0,i)取子串,如果子串[0,k)存在解,而[k,i)又在字典里面的话,那么[0,i)就是有解的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class Solution {
     
        public boolean wordBreak(String s, Set<String> dict) {
            // boolean数组mem[i]表示[0,i)是否存在通路(0<=i<=str.length)
            boolean[] mem = new boolean[s.length()+1];
            mem[0] = true;
     
            // mem[i] = {所有 mem[k] && ([k,i)∈dict) 中只要有一个为true,就为true}(其中0<=k<i)
            for (int i=0; i<=s.length(); i++) {
                for (int k=0; k<i; k++) {
                    String str = s.substring(k, i);
                    mem[i] = mem[k] && dict.contains(str);
     
                    if (mem[i])
                        break;
                }
            }
     
            return mem[s.length()];
        }
     
    }

    Validate Binary Search Tree

    【题目】Given a binary tree, determine if it is a valid binary search tree (BST).

    Assume a BST is defined as follows:

    • The left subtree of a node contains only nodes with keys less than the node’s key.
    • The right subtree of a node contains only nodes with keys greater than the node’s key.
    • Both the left and right subtrees must also be binary search trees.

    【解答】递归检查。对每一个节点都尝试寻找左孩子的最小值作为以该节点为根的树的最小值;对每一个节点都尝试寻找右孩子的最大值作为以该节点为根的树的最大值。对于非法的BST,也没有必要继续执行下去了,抛出InvalidBSTException异常。使用异常机制就可以免去定义一个表示非法BST的特殊返回值来特殊处理。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    class InvalidBSTException extends RuntimeException {
    }
    public class Solution {
        public boolean isValidBST(TreeNode root) {
            if (null==root)
                return true;
     
            try {
                validateBST(root);
            } catch (InvalidBSTException e) {
                return false;
            }
     
            return true;
        }
     
        private int maxValueOfValidBST(TreeNode root) {
            validateBST(root);
     
            if (root.right!=null) {
                return maxValueOfValidBST(root.right);
            }
     
            return root.val;
        }
     
        private int minValueOfValidBST(TreeNode root) {
            validateBST(root);
     
            if (root.left!=null) {
                return minValueOfValidBST(root.left);
            }
     
            return root.val;
        }
     
        private void validateBST(TreeNode root) {
            if (root.left!=null) {
                int maxLeft = maxValueOfValidBST(root.left);
                if (maxLeft>=root.val)
                    throw new InvalidBSTException();
            }
            if (root.right!=null) {
                int minRight = minValueOfValidBST(root.right);
                if (minRight<=root.val)
                    throw new InvalidBSTException();
            }
        }
    }

    Insertion Sort List

    【题目】Sort a linked list using insertion sort.

    【解答】引入一个假的哨兵节点,放在头上,这样每次在插入节点的时候,就不需要单独判断新插入的那个节点是不是head,代码简单一些:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class Solution {
        public ListNode insertionSortList(ListNode head) {
            ListNode fakeHead = new ListNode(Integer.MIN_VALUE);
     
            ListNode cur = head;
            while (cur!=null) {
                ListNode next = cur.next;
                insert(fakeHead, cur);
                cur = next;
            }
     
            return fakeHead.next;
        }
     
        private void insert(ListNode fakeHead, ListNode cur) {
            ListNode node = fakeHead;
            while (node.next!=null && node.next.val<cur.val)
                node = node.next;
     
            cur.next = node.next;
            node.next = cur;
        }
    }

    Integer to Roman

    【题目】Given an integer, convert it to a roman numeral.

    Input is guaranteed to be within the range from 1 to 3999.

    【解答】刚开始我想寻找罗马数字的规则,罗马数字计数法讨厌的地方在于,有的数的表示不是通过“加”,而是通过“减”来实现的,比如4,是5-1:IV。最后代码需要考虑字符串生长的方向,写得很复杂,还有些case是错误的。

    重新在草稿上写了一些罗马数字,发现这些“减”的情况极其有限,说白了,就只有4(IV)、9(IX)、40(XL)、90(XC)、400(CD)和900(CM),这几个特殊情况,再加上罗马字母本身的字符,就足以枚举出从1到3999所有的情况了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    public class Solution {
        public String intToRoman(int num) {
            Map<Integer, String> map = new HashMap<>();
            map.put(1, "I");
            map.put(4, "IV");
            map.put(5, "V");
            map.put(9, "IX");
            map.put(10, "X");
            map.put(40, "XL");
            map.put(50, "L");
            map.put(90, "XC");
            map.put(100, "C");
            map.put(400, "CD");
            map.put(500, "D");
            map.put(900, "CM");
            map.put(1000, "M");
     
            int enums[] = {1000,900,500,400,100,90,50,40,10,9,5,4,1};
     
            String result = "";
            for (int i=0; i<enums.length; i++) {
                int quotient = num/enums[i];
                num = num%enums[i];
     
                for(int j=1; j<=quotient; j++)
                    result += map.get(enums[i]);
            }
     
            return result;
        }
    }

    代码一下子很简单,有豁然开朗的感觉。所以,数学题目做多了也会把一些简单的问题复杂化。要去根据数的大小分类,考虑情况讨论就陷进去了。

    4Sum

    【题目】Given an array S of n integers, are there elements abc, and d in S such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.

    Note:

    • Elements in a quadruplet (a,b,c,d) must be in non-descending order. (ie, a ≤ b ≤ c ≤ d)
    • The solution set must not contain duplicate quadruplets.
        For example, given array S = {1 0 -1 0 -2 2}, and target = 0.
    
        A solution set is:
        (-1,  0, 0, 1)
        (-2, -1, 1, 2)
        (-2,  0, 0, 2)

    【解答】其实有了前面3Sum的基础,这道题就很好做了,只不过在3Sum的基础上外面多套一层循环而已:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    public class Solution {
        public List<List<Integer>> fourSum(int[] num, int target) {
            Arrays.sort(num);
     
            List<List<Integer>> list = new ArrayList<List<Integer>>();
            for (int i = 0; i <= num.length - 3; i++) {
                if (i != 0 && num[i] == num[i-1])
                    continue;
     
                for (int j = i + 1; j <= num.length - 2; j++) {             
                    if (j != i + 1 && num[j] == num[j-1])
                        continue;
     
                    int left = j + 1, right = num.length - 1;
                    while (left < right) {
                        if (left != j + 1 && num[left] == num[left-1]) {
                            left++;
                            continue;
                        }
                        if (right != num.length - 1 && num[right] == num[right+1]) {
                            right--;
                            continue;
                        }
     
                        int sum = num[i] + num[j] + num[left] + num[right];
                        if (sum == target) {
                            List<Integer> item = new ArrayList<Integer>();
                            item.add(num[i]);
                            item.add(num[j]);
                            item.add(num[left]);
                            item.add(num[right]);
     
                            list.add(item);
                            left++;
                            right--;
                        } else if (sum < target) {
                            left++;
                        } else {
                            right--;
                        }
                    } // while
                } // for:j
            } // for: i
     
            return list;
        }
    }

    Jump Game

    【题目】Given an array of non-negative integers, you are initially positioned at the first index of the array.

    Each element in the array represents your maximum jump length at that position.

    Determine if you are able to reach the last index.

    For example:
    A = [2,3,1,1,4], return true.

    A = [3,2,1,0,4], return false.

    【解答】这题看起来很容易,我最开始的思路是建立一个用作DP的reachable数组,然后从A的最左侧开始遍历,对于A中每一个元素,所代表的能遍历到的后面的元素全部把reachable全部置为true,但是遇到A很大的某一个变态case的时候,执行超时了。

    所以就着这个思路改进一下,其实对于每一个可达的A中的元素来说,都可以唯一确定一个下一步可以达到的最大的范围,如果这个最大范围包含了A数组的末尾,那就返回true:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Solution {
        public boolean canJump(int[] A) {
            int end = 0;
            for (int i=0; i<A.length; i++) {
                if (i>end)
                    continue;
     
                if (A[i]+i > end)
                    end = A[i]+i;
     
                if (end>=A.length-1)
                    return true;
            }
     
            return false;
        }
    }

    Add Two Numbers

    【题目】You are given two linked lists representing two non-negative numbers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.

    Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
    Output: 7 -> 0 -> 8

    【解答】处理好进位就好了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    public class Solution {
        public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
            int carry = 0;
            ListNode n1 = l1;
            ListNode n2 = l2;
            ListNode head = null;
            ListNode result = null;
     
            while (n1!=null || n2!=null) {
                int sum = carry;
                if (n1!=null) {
                    sum += n1.val;
                    n1 = n1.next;
                }
                if (n2!=null) {
                    sum += n2.val;
                    n2 = n2.next;
                }
     
                if (sum/10>0)
                    carry = 1;
                else
                    carry = 0;
     
                sum = sum % 10;
     
                ListNode item = new ListNode(sum);
                if (result==null) {
                    head = item;
                }
                else {
                    result.next = item;
                }
                result = item;
            }
     
            if (carry!=0)
                result.next = new ListNode(1);
     
            return head;
        }
    }

    Anagrams

    【题目】Given an array of strings, return all groups of strings that are anagrams.

    Note: All inputs will be in lower-case.

    【解答】Anagram——我也是查到的含义,anagram是一个中文里没有的概念(因为中文由字、而非字母组成),指一个单词、短语或句子经过字母重组后产生出来的另一个单词,短语或句子。比如,won是now的anagram,satin是stain的anagram,a net 是neat的anagram。

    知道这个以后,只要保证同样字母组成的词语能够被放到一起统计就好了,引入一个HashMap,让每个词里面的字符数组按照字母顺序重新排好序,这构成的新词就可以拿来当做key了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class Solution {
        public List<String> anagrams(String[] strs) {
            Map<String, List<String>> map = new HashMap<>();
            for (String str : strs) {
                char[] arr = str.toCharArray();
                Arrays.sort(arr);
                String s = new String(arr);
     
                if (!map.containsKey(s))
                    map.put(s, new ArrayList<String>());
                map.get(s).add(str);
            }
     
            List<String> list = new ArrayList<>();
            for (Map.Entry<String, List<String>> entry : map.entrySet()) {
                if (entry.getValue().size()>1) {
                    list.addAll(entry.getValue());
                }
            }
     
            return list;
        }
    }

    Decode Ways

    【题目】A message containing letters from A-Z is being encoded to numbers using the following mapping:

    'A' -> 1
    'B' -> 2
    ...
    'Z' -> 26

    Given an encoded message containing digits, determine the total number of ways to decode it.

    For example,
    Given encoded message "12", it could be decoded as "AB" (1 2) or "L" (12).

    The number of ways decoding "12" is 2.

    【解答】用一个数组来帮助DP,区分两位数表示的字母和一位数表示的字母这两种可能性:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    public class Solution {
        public int numDecodings(String s) {
            if (null==s || "".equals(s))
                return 0;
     
            int[] arr = new int[s.length()];
            int prev = '0';
            for (int i=0; i<s.length(); i++) {
                int ch = s.charAt(i);
                int count = 0;
     
                // two digits
                int num = 10*(prev-'0') + (ch-'0');
                int c = 'A'+num-1;
                int prevC = 'A'+(prev-'0')-1;
                if (c>='A' && c<='Z' && prevC>='A' && prevC<='Z') {
                    if (i>1)
                        count += arr[i-2];
                    else if(i==1)
                        count += 1;
                }
     
                // one digit
                c = 'A'+(ch-'0')-1;
                if (c>='A' && c<='Z') {
                    if (i>0)
                        count += arr[i-1];
                    else
                        count += 1;
                }
     
                if (count==0)
                    return 0;
     
                arr[i] = count;
                prev = ch;
            }
     
            return arr[s.length()-1];
        }
    }

    Letter Combinations of a Phone Number

    【题目】Given a digit string, return all possible letter combinations that the number could represent.

    A mapping of digit to letters (just like on the telephone buttons) is given below.

    Input:Digit string "23"
    Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

    Note:
    Although the above answer is in lexicographical order, your answer could be in any order you want.

    【解答】注意入参为空字符串的情况,还有电话上面按键1这两个特殊的情况的处理就好了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    public class Solution {
        public List<String> letterCombinations(String digits) {
            List<char[]> list = new ArrayList<>();
            list.add(new char[]{' '});
            list.add(new char[]{});
            list.add(new char[]{'a','b','c'});
            list.add(new char[]{'d','e','f'});
            list.add(new char[]{'g','h','i'});
            list.add(new char[]{'j','k','l'});
            list.add(new char[]{'m','n','o'});
            list.add(new char[]{'p','q','r','s'});
            list.add(new char[]{'t','u','v'});
            list.add(new char[]{'w','x','y','z'});
     
            List<String> ret = new ArrayList<>();
            ret.add("");
     
            return build(ret, list, digits, 0);
        }
     
        private List<String> build(List<String> ret, List<char[]> list, String digits, int pos) {
            if (pos==digits.length())
                return ret;
     
            int num = digits.charAt(pos) - '0';
            char[] chs = list.get(num);
            if (chs.length==0)
                return ret;
     
            List<String> newRet = new ArrayList<>();
            for (char c : chs) {
                for (String str : ret) {
                    newRet.add(str+c);
                }
            }
     
            return build(newRet, list, digits, pos+1);
        }
    }

    Linked List Cycle

    【题目】Given a linked list, determine if it has a cycle in it.

    Follow up:
    Can you solve it without using extra space?

    【解答】准备一个快指针和一个慢指针,慢指针每次走一步,快指针每次走两步,如果他们俩碰上,那就存在环;如果走完了也没碰上,那就不存在环:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class Solution {
        public boolean hasCycle(ListNode head) {
            if(null==head)
                return false;
     
            ListNode first = head, second = head;
            while (true) {
                if(null==first.next || null==first.next.next)
                    return false;
     
                first = first.next.next;
                second = second.next;
     
                if(first==second)
                    return true;
            }
     
        }
    }

    Linked List Cycle II

    【题目】Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

    Follow up:
    Can you solve it without using extra space?

    【解答】问题基于Linked List Cycle,快慢两个指针的模式依然保留。下面这个复杂度低,比较好的解法来自水中的鱼的这篇博客,具体原理其中已经说得非常清楚了。大致的思路,如果在图上画一画,会清楚起来:

    1. 快指针每次走两步,慢指针每次走一步,快慢两个指针第一次碰面以后,让慢指针退回起点;
    2. 然后快指针和慢指针每次都走一步,它们碰面的地方,就是这个环的入口。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public class Solution {
        public ListNode detectCycle(ListNode head) {
            if (null==head)
                return null;
     
            ListNode x = head;
            ListNode y = head;
     
            do {
                x = x.next;
                y = y.next;
                if (y==null)
                    return null;
                y = y.next;
                if (y==null)
                    return null;
            } while (x!=y);
            // found the cycle begin point
     
            // special case, the whole list is a cycle
            if (head==x)
                return head;
     
            x = head;
            do {
                x = x.next;
                y = y.next;
            } while (x!=y);
     
            return x;
        }
    }

    Best Time to Buy and Sell Stock

    【题目】Say you have an array for which the ith element is the price of a given stock on day i.

    If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.

    【解答】一买一卖,求最大差价。这里有一个隐含条件,就是买必须发生在卖之前,因此这一段时间内,找一个切分点,使得后半段的最大值减去前半段的最小值这个差最大。因此在填最大值表的时候,要从右往左;而填最小值表的时候,要从左往右:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public class Solution {
        public int maxProfit(int[] prices) {
            if (prices.length<=1) {
                return 0;
            }
     
            Integer[] mins = new Integer[prices.length]; // first n days
            Integer[] maxs = new Integer[prices.length]; // last n days
            mins[0] = prices[0];
            maxs[prices.length-1] = prices[prices.length-1];
     
            // fill max prices, from right to left
            for (int i=prices.length-2; i>=0; i--) {
                if (prices[i]>maxs[i+1])
                    maxs[i] = prices[i];
                else
                    maxs[i] = maxs[i+1];
            }
     
            // fill min prices, and calculate the biggest profit, from left to right
            int biggestProfit = 0;
            for (int i=1; i<prices.length; i++) {
                if (prices[i]<mins[i-1])
                    mins[i] = prices[i];
                else
                    mins[i] = mins[i-1];
     
                int profit = maxs[i] - mins[i];
                if (profit>biggestProfit)
                    biggestProfit = profit;
            }
     
            return biggestProfit;
        }
    }

    Unique Paths II

    【题目】Follow up for “Unique Paths”:

    Now consider if some obstacles are added to the grids. How many unique paths would there be?

    An obstacle and empty space is marked as 1 and 0 respectively in the grid.

    For example,

    There is one obstacle in the middle of a 3×3 grid as illustrated below.

    [
      [0,0,0],
      [0,1,0],
      [0,0,0]
    ]

    The total number of unique paths is 2.

    Note: m and n will be at most 100.

    【解答】有了前面一道Unique Paths的求解,这道题就简单多了。这个问题是那个问题的加强,面对的是存在若干路障的情况。我还是利用动态规划求解,需要注意的是路障设在起点和终点这两个特殊的情况:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    public class Solution {
        public int uniquePathsWithObstacles(int[][] obstacleGrid) {
            if(obstacleGrid.length==0 || obstacleGrid[0].length==0 || obstacleGrid[0][0]==1 || obstacleGrid[obstacleGrid.length-1][obstacleGrid[0].length-1]==1)
                return 0;
     
            Integer[][] visited = new Integer[obstacleGrid.length][obstacleGrid[0].length];
            return traverse(0, 0, obstacleGrid, visited);
        }
     
        private int traverse(int i, int j, int[][] obstacleGrid, Integer[][] visited) {
            if (i==obstacleGrid.length-1 && j==obstacleGrid[0].length-1)
                return 1;
     
            int count = 0;
     
            if (i<obstacleGrid.length-1 && obstacleGrid[i+1][j]==0) {
                Integer val = visited[i+1][j];
                if (val==null) {
                    val = traverse(i+1, j, obstacleGrid, visited);
                    visited[i+1][j] = val;
                }
                count += val;
            }
     
            if (j<obstacleGrid[0].length-1 && obstacleGrid[i][j+1]==0) {
                Integer val = visited[i][j+1];
                if (val==null) {
                    val = traverse(i, j+1, obstacleGrid, visited);
                    visited[i][j+1] = val;
                }
                count += val;
            }
     
            return count;
        }
    }

    Longest Palindromic Substring

    【题目】Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.

    【解答】求最长回文子串。下面的解法是O(n*n)的,最容易想到和写出来,注意需要考虑到奇数对称(比如aba)和偶数对称(比如abba)这两种情况。除此之外还有两种复杂度更低的解法,后续可以专门总结一下:

    • 早就听说这个问题有O(n)的解法,后来查了一下叫做Manacher算法,我是看着这篇博客理解的。
    • 还有一种思路上也很容易理解的解法是把字符串翻转,再把原字符串和翻转后的字符串分别建立后缀树,在对每一个字符视作回文中心,到两个后缀树中去寻找最长的共同前缀。我原以为这种解法也是O(n*n)的,但是其实建立后缀树有O(n)的办法,所以这个方法的复杂度也可以是O(n)的。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public class Solution {
        public String longestPalindrome(String s) {
            String sub = "";
            for (int i=0; i<s.length(); i++) {
                int j;
                // odd
                for (j=0; i-j>=0 && i+j<s.length(); j++) {
                    if (s.charAt(i-j)==s.charAt(i+j)) {
                        if (j*2+1>sub.length())
                            sub = s.substring(i-j, i+j+1);
                    } else {
                        break;
                    }
                }
     
                // even
                for (j=0; i-j>=0 && i+1+j<s.length(); j++) {
                    if (s.charAt(i-j)==s.charAt(i+j+1)) {
                        if ((j+1)*2>sub.length())
                            sub = s.substring(i-j, i+j+2);
                    } else {
                        break;
                    }
                }
            }
     
            return sub;
        }
    }

    Longest Substring Without Repeating Characters

    【题目】Given a string, find the length of the longest substring without repeating characters. For example, the longest substring without repeating letters for “abcabcbb” is “abc”, which the length is 3. For “bbbbb” the longest substring is “b”, with the length of 1.

    【解答】大致思路是用两个指针,快指针用来一直向前探路,慢指针用来标注这个最长子串的起始位置;再引入一个map,key存放字符,value存放字符位置,一旦发现快指针的字符在map中存在,就说明找到了一个疑似最大子串,可以和积累的最大值进行比较。

    第一遍实现的时候执行超时了,原因是在下面代码的map.containsKey(ch)为真的时候,我直接把map清空,再把fast放到newSlow的下一个的位置,这会导致从slow到newSlow这一段重复比较,所以我做了优化——在这种情况下把slow到newSlow的记录从map中清掉即可:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    public class Solution {
        public int lengthOfLongestSubstring(String s) {
            int slow = 0;
            int fast = 0;
            int max = 0;
            Map<Character, Integer> map = new HashMap<>();
     
            while (fast<s.length()) {
                char ch = s.charAt(fast);
     
                if (fast==slow) {
                    map.put(ch, fast);
                    fast++;
                    continue;
                }
     
                if (map.containsKey(ch)) {
                    max = Math.max(max, fast-slow);
     
                    // remove the chars ∈ [slow, newSlow)
                    int newSlow = map.get(ch) + 1;
                    for (int i=slow; i<newSlow; i++) {
                        map.remove(s.charAt(i));
                    }
     
                    slow = newSlow;
     
                } else {
                    map.put(ch, fast);
                    fast++;
                }
            }
     
            return Math.max(max, fast-slow);
     
        }
    }

    Unique Paths

    【题目】A robot is located at the top-left corner of a m x n grid (marked ‘Start’ in the diagram below).

    The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked ‘Finish’ in the diagram below).

    How many possible unique paths are there?

    Above is a 3 x 7 grid. How many possible unique paths are there?

    Note: m and n will be at most 100.

    【解答】前面做了几道回溯法的题目以后,这道题我一开始也用单纯的回溯法,但是最后超时了。再看了看题目,其实可以引入动态规划,用一个Integer的数组来记录位置(i,j)上面路径的数量,避免重复计算,其中初始值为null表示尚未计算:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    public class Solution {
        public int uniquePaths(int m, int n) {
            if (m<=0 || n<=0)
                return 0;
     
            Integer[][] visited = new Integer[m][n];
            return traverse(0, 0, m, n, visited);
        }
     
        private int traverse(int i, int j, int m, int n, Integer[][] visited) {
            if (i==m-1 && j==n-1)
                return 1;
     
            int count = 0;
     
            if (i<m-1) {
                Integer val = visited[i+1][j];
                if (val==null) {
                    val = traverse(i+1, j, m, n, visited);
                    visited[i+1][j] = val;
                }
                count += val;
            }
     
            if (j<n-1) {
                Integer val = visited[i][j+1];
                if (val==null) {
                    val = traverse(i, j+1, m, n, visited);
                    visited[i][j+1] = val;
                }
                count += val;
            }
     
            return count;
        }
    }

    但是,就这道题而言,其实有更优雅的办法来解决,就是利用排列组合的特性:

    观察从左上角到右下角的各条路径,它们都有个共同的特点,向右走的步数都是一样的(stepRight = m-1步),向下走的步数也都是一样的(stepDown = n-1步),总步数stepAll = stepRight + stepDown——因此问题就变成了一个组合问题,即从stepAll的总步数里面,选出stepRight步来,共有几种选择的方法——C(stepAll, stepRight) = C(m+n-2, m-1)。然后再根据组合公式 C(a,b) = a!/(b!*(a-b)!) 求解。

    Unique Binary Search Trees II

    【题目】Given n, generate all structurally unique BST’s (binary search trees) that store values 1…n.

    For example,
    Given n = 3, your program should return all 5 unique BST’s shown below.

       1         3     3      2      1
               /     /      /       
         3     2     1      1   3      2
        /     /                        
       2     1         2                 3

    【解答】在下面那道Unique Binary Search Trees的基础上,这题就好做了。每次选定一个根元素,然后把所有左子树的可能和右子树的可能结合起来,放到结果里面去:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    public class Solution {
        public List<TreeNode> generateTrees(int n) {
            if (n==0) {
                List<TreeNode> list = new ArrayList<>();
                list.add(null);
                return list;
            }
            return generateTreesByNumberRange(1, n);
        }
     
        private List<TreeNode> generateTreesByNumberRange(int start, int end) {
            List<TreeNode> list = new ArrayList<>();
            if (start==end) {
                list.add(new TreeNode(start));
                return list;
            }
     
            for (int i=start; i<=end; i++) {
                List<TreeNode> left = null;
                if (i!=start) {
                    left = generateTreesByNumberRange(start, i-1);
                }
     
                List<TreeNode> right = null;
                if (i!=end) {
                    right = generateTreesByNumberRange(i+1, end);
                }
     
                if (left==null) {
                    for (TreeNode node : right) {
                        TreeNode root = new TreeNode(i);
                        root.right = node;
                        list.add(root);
                    }
                } else if (right==null) {
                    for (TreeNode node : left) {
                        TreeNode root = new TreeNode(i);
                        root.left = node;
                        list.add(root);
                    }
                } else {
                    for (TreeNode n1 : left) {
                        for (TreeNode n2 : right) {
                            TreeNode root = new TreeNode(i);
                            root.left = n1;
                            root.right = n2;
                            list.add(root);
                        }
                    } // for
                } // else
            } // for
     
            return list;
        }
    }

    Unique Binary Search Trees

    【题目】Given n, how many structurally unique BST’s (binary search trees) that store values 1…n?

    For example,
    Given n = 3, there are a total of 5 unique BST’s.

       1         3     3      2      1
               /     /      /       
         3     2     1      1   3      2
        /     /                        
       2     1         2                 3

    【解答】二叉搜索树的每一个节点都满足:如果有左孩子,那么左孩子小于自己;如果有右孩子,那么右孩子小于自己。假设f(n)表示有多少棵树,利用动态规划:

    • 当n=0,就一棵树(空树);
    • 当n=1,就一棵树;
    • 当n=2,f(2) = 左子树f(0)*右子数f(1) + 左子树f(1)*右子数f(0),即根节点可以取第一个数,也可以取第二个数,总数就是这两种情况相加;
    • ……
    • 当n=i,f(i) = Σ total(k)*total(i-1-k),其中k=0..i-1。

    有了这样的递推式就可以写代码了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Node {
        public Node left;
        public Node right;
        public int value;
    }
    public class Solution {
        public int numTrees(int n) {
            if(n<0)
                throw new IllegalArgumentException();
            int[] total = new int[n+1];
            total[0]=total[1]=1;
            for(int i=2; i<=n; i++){
                for(int k=0; k<i; k++)
                    total[i] += total[k]*total[i-1-k];
            }
            return total[n];
        }
    }

    Two Sum

    【题目】Given an array of integers, find two numbers such that they add up to a specific target number.

    The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.

    You may assume that each input would have exactly one solution.

    Input: numbers={2, 7, 11, 15}, target=9
    Output: index1=1, index2=2

    【解答】我觉得至少有两个常见的思路:

    一个是先排序,然后利用双指针,一个从小头开始右移,一个从大头开始左移,直到找到和target匹配的位置为止,这和3 sum、4 sum的做法是一致的;

    另一个思路是,不用排序了,把这堆数放到HashMap里面去,其中key是这个数的值,value是下标,然后把原数组中的每一个数都去找它的补,使得它加上它的补等于目标值,如果这个补在HashMap里面,就匹配上了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class Solution {
        public int[] twoSum(int[] numbers, int target) {
            Map<Integer,Integer> map = new HashMap<Integer, Integer>();
            for (int i=0;i<numbers.length;i++) {
                map.put(numbers[i], i);
            }
     
            for (int i=0;i<numbers.length;i++) {
                int dif = target-numbers[i];
                Integer index = map.get(dif);
     
                if (index!=null && i!=index) {
                    if (index>i)
                        return new int[]{i+1,index+1};
                    else
                        return new int[]{index+1,i+1};
                }
            }
     
            return null;
        }
    }

    Convert Sorted List to Binary Search Tree

    【题目】Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST.

    【解答】有了前面的Convert Sorted Array List to Binary Search Tree那道题的启发,总的思路就是,把这个LinkedList先放到一个ArrayList里面去,然后和那道题的解法就一样了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    public class Solution {
        public TreeNode sortedListToBST(ListNode head) {
            if (null==head)
                return null;
     
            List<TreeNode> list = new ArrayList<>();
            ListNode node = head;
            while(node!=null) {
                TreeNode n = new TreeNode(node.val);
                list.add(n);
                node = node.next;
            }
     
            return buildTree(0, list.size()-1, list);
        }
     
        private TreeNode buildTree(int start, int end, List<TreeNode> list) {
            int mid = (start+end)/2;
            TreeNode root = list.get(mid);
            if (start<=mid-1)
                root.left = buildTree(start, mid-1, list);
            if (end>=mid+1)
                root.right = buildTree(mid+1, end, list);
     
            return root;
        }
    }

    Maximum Product Subarray

    【题目】Find the contiguous subarray within an array (containing at least one number) which has the largest product.

    For example, given the array [2,3,-2,4],
    the contiguous subarray [2,3] has the largest product = 6.

    【解答】有了前面的Maximum Subarray的提示,这道题就简单多了。方法类似,但是这里求的最值是乘积,所以需要考虑,如果有两个比较大的负数,乘起来是有可能成为最大值的,所以每次存储前一次结果的时候,不仅要存正的最大值,还要存负的最小值:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class Solution {
        public int maxProduct(int[] A) {
            int max = Integer.MIN_VALUE;
            int negative = 1;
            int positive = 1;
     
            for (int a : A) {
                int n1 = negative*a;
                int n2 = positive*a;
                int n3 = a;
     
                positive = Math.max(n1, n2);
                positive = Math.max(positive, n3);
     
                negative = Math.min(n1, n2);
                negative = Math.min(negative, n3);
     
                max = Math.max(max, positive);
            }
     
            return max;
        }
    }

    Maximum Subarray

    【题目】Find the contiguous subarray within an array (containing at least one number) which has the largest sum.

    For example, given the array [−2,1,−3,4,−1,2,1,−5,4],
    the contiguous subarray [4,−1,2,1] has the largest sum = 6.

    【解答】假设f(i)表示区间[0,i]内,以A[i]结尾的子串的最大值,那可以找到从f(i)到f(i+1)的递推关系:

    • 如果A[i+1]+f(i)>0,那么f(i+1)=A[i+1]+f(i),因为f(i+1)要以A[i+1]结尾;
    • 但是如果A[i+1]+f(i)<0,那我可以扔掉这个子串,也就是说,就是一个空串(””)的和0都比这个子串的sum大。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class Solution {
     
        public int maxSubArray(int[] A) {
            if (null==A)
                throw new IllegalArgumentException();
     
            int max = Integer.MIN_VALUE;
            int sum = 0;
            // think about every maximum subarray ended with i
            for (int i : A) {
                sum += i;
     
                // keep it
                if(sum>max)
                    max = sum;
     
                // deprecate it, the worst case: every member in A are negative, then the result is 0
                if(sum<0)
                    sum = 0;
            }
     
            return max;
        }
     
    }

    Triangle

    【题目】Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.

    For example, given the following triangle

    [
         [2],
        [3,4],
       [6,5,7],
      [4,1,8,3]
    ]

    The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).

    【解答】一层一层从上往下遍历,用一个List来存放最值串:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    public class Solution {
        public int minimumTotal(List<List<Integer>> triangle) {
            List<Integer> mins = new ArrayList<>();
            int minTotal = Integer.MAX_VALUE;
            for (int index=0; index<triangle.size(); index++) {
                List<Integer> list = triangle.get(index);
                List<Integer> newMins = new ArrayList<>();
                for (int i=0; i<list.size(); i++) {
                    int left = Integer.MAX_VALUE;
                    if (i!=0)
                        left = mins.get(i-1) + list.get(i);
     
                    int right = Integer.MAX_VALUE;
                    if (i!=list.size()-1)
                        right = mins.get(i) + list.get(i);
     
                    int min;
                    if (list.size()==1)
                        min = list.get(0);
                    else
                        min = left<right? left : right;
                    if (index==triangle.size()-1) {
                        if (minTotal>min)
                            minTotal = min;
                    }
                    else {
                        newMins.add(min);
                    }
                }
     
                mins = newMins;
            }
     
            return minTotal;
        }
    }

    Best Time to Buy and Sell Stock II

    【题目】Say you have an array for which the ith element is the price of a given stock on day i.

    Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

    【解答】看似是在前一道题Best Time to Buy and Sell Stock的基础上,但是其实要简单得多。每次在买之前,必须先卖掉,那就意味着,其中的任意时间,只要明天的价格高于今天,那今天买明天卖一定是最划算的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Solution {
        public int maxProfit(int[] prices) {
            int total = 0;
            if(prices.length==0 || prices.length==1)
                return 0;
     
            for (int i=0; i<prices.length-1; i++)
                if(prices[i+1]-prices[i]>0)
                    total += prices[i+1]-prices[i];
     
            return total;
        }
    }

    Swap Nodes in Pairs

    【题目】Given a linked list, swap every two adjacent nodes and return its head.

    For example,
    Given 1->2->3->4, you should return the list as 2->1->4->3.

    Your algorithm should use only constant space. You may not modify the values in the list, only nodes itself can be changed.

    【解答】三个指针,第一个占住位置,后两个指向的元素交换一下。需要注意边界上的处理:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    public class Solution {
        public ListNode swapPairs(ListNode head) {
            if (head == null || head.next == null)
                return head;
     
            ListNode first = null;
            ListNode second = head;
            ListNode third = head.next;
     
            while(third!=null) {
                ListNode x = swap(second, third);
                if (first==null)
                    head = x;
                else
                    first.next = x;
     
                ListNode temp = second;
                second = third;
                third = temp;
     
                if (third.next==null)
                    return head;
                else {
                    first = third;
                    third = third.next.next;
                    second = second.next.next;
                }
            }
     
            return head;
        }
     
        private ListNode swap(ListNode second, ListNode third) {
            second.next = third.next;
            third.next = second;
     
            return third;
        }
    }

    Convert Sorted Array to Binary Search Tree

    【题目】Given an array where elements are sorted in ascending order, convert it to a height balanced BST.

    【解答】找整个有序序列的中点,把中点作为最大的根,然后对左、右两个子序列分别递归执行构造树的过程:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class Solution {
        public TreeNode sortedArrayToBST(int[] num) {
            if(num==null)
                return null;
            return buildTree(num, 0, num.length-1);
        }
     
        private TreeNode buildTree(int[] num, int start, int end) {
            if (end<start)
                return null;
     
            int mid = (start+end)/2;
            TreeNode root = new TreeNode(num[mid]);
     
            root.left = buildTree(num, start, mid-1);
            root.right = buildTree(num, mid+1, end);
     
            return root;
        }
    }

    Container With Most Water

    【题目】Given n non-negative integers a1a2, …, an, where each represents a point at coordinate (iai). n vertical lines are drawn such that the two endpoints of line i is at (iai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.

    Note: You may not slant the container.

    【解答】求最大体积的公式很容易写:V = Max( Min(height[j], height[i])*(j-i) ),但硬算复杂度就是n的平方,但是考虑到体积是由(j-i)和短木板的乘积得到的,因此在短木板确定的情况下,另一条木板再长也没用。使用左右两个双指针对向移动来遍历:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Solution {
        // V = Max( Min(height[j], height[i])*(j-i) )
        public int maxArea(int[] height) {
            int max = 0;
            int i=0, j=height.length-1;
     
            while (i<j) {
                max = Math.max(Math.min(height[j],height[i])*(j-i), max);
                if (height[i]>height[j])
                    j--;
                else
                    i++;
            }
     
            return max;
        }
    }

    Minimum Path Sum

    【题目】Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.

    Note: You can only move either down or right at any point in time.

    【解答】和前面Jump Game那道题的思路差不多,动态规划求解,从终点开始往起点推:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public class Solution {
        public int minPathSum(int[][] grid) {
            int[][] visited = new int[grid.length][grid[0].length];
            return min(grid, visited, grid.length-1, grid[0].length-1);
        }
     
        private int min(int[][] grid, int[][]visited, int i, int j){
            if(i==0 && j==0)
                return grid[0][0];
     
            int min = Integer.MAX_VALUE;
            if(i>0) {
                int val;
                if(visited[i-1][j]>0)
                    val = visited[i-1][j] + grid[i][j];
                else
                    val = min(grid, visited, i-1, j) + grid[i][j];
     
                min = Math.min(val, min);
            }
     
            if(j>0) {
                int val;
                if(visited[i][j-1]>0)
                    val = visited[i][j-1] + grid[i][j];
                else
                    val = min(grid, visited, i, j-1) + grid[i][j];
     
                min = Math.min(val, min);
            }
     
            visited[i][j] = min;
            return min;
        }
    }

    Surrounded Regions

    【题目】Given a 2D board containing 'X' and 'O', capture all regions surrounded by 'X'.

    A region is captured by flipping all 'O's into 'X's in that surrounded region.

    For example,

    X X X X
    X O O X
    X X O X
    X O X X

    After running your function, the board should be:

    X X X X
    X X X X
    X X X X
    X O X X

    【解答】这道题我刚拿到的时候想,就是要找连通关系嘛,所以最开始的做法是用一个m*n的表格记录每个点访问的状态,包括:

    1. 未考察状态
    2. 正在考察周边元素时的pending状态
    3. 元素为’O’且不需要flip
    4. 元素为’O’且需要flip
    5. 元素为’X’(这种状态处理起来最简单,也可以不记录)

    然后尝试递归求解,但是很快就栈溢出了——,我看了一下导致溢出的那个case,全部都是’O’,这也难怪了,用这种方法每一次遇到’O’都无法确定,必须递归调用检查周边的’O’,而所有的’O’都连通,因此直到所有的’O’都遍历到为止,根本无法确定其中任何一个’O’是需要flip的。

    我想了一下,对于这种极端的case,用递归求解的话肯定会爆栈的。只能用循环求解。另一方面,题目中有这样一个隐含的规律:

    如果’O’的区域和整个board的边界连通,那么该区域就是不可flip的;反之,则是需要flip的。

    所以,我的新办法的思路基本上是这样:

    1. 先找整个board的边界,以边界上那些’O’作为起点,放到一个ArrayList里面去,且置它的初始游标位置为0;
    2. 每次循环遍历这个ArrayList,但不需要全部遍历,而是从游标处开始,一直遍历到ArrayList的结尾,对每一个元素,也就是上一次存储的’O’,找其中的每个’O’的周边:
      1. 如果周边为’X’,无视;
      2. 如果周边为’O’,但已经被访问过(在这个ArrayList里面存在),也无视;
      3. 如果周边为’O’,且没有被访问过,把这个’O’元素加到这个ArrayList里面。

    如此进行,直到最后一次没有元素添加进来,这样,这个ArrayList里面剩下的元素,都是无法被flip的元素了,那么整个board里面余下的’O’都是要被flip的。

    但是,这整个过程里面需要经常判断某元素是否在这个ArrayList里面,为了节约这一步的开销,引入一个HashSet,用来判断这个存在性。另外,在set中比较元素的时候,由于是自定义的元素Item,需要覆写它的equals和hashCode方法,使得相同i、j坐标的元素可以落到一个hash桶里面执行比较:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    class Item {
        public static int UP = 1;
        public static int LEFT = 2;
        public static int DOWN = 3;
        public static int RIGHT = 4;
     
        public int from; // UP, LEFT, DOWN, RIGHT
        public int i;
        public int j;
     
        public Item(int i, int j, int from) {
            this.i = i;
            this.j = j;
            this.from = from;
        }
     
        @Override
        public boolean equals(Object obj) {
            Item item = (Item) obj;
            return item.i == i && item.j == j;
        }
     
        @Override
        public int hashCode() {
            return i + j;
        }
    }
     
    public class Solution {
     
        public void solve(char[][] board) {
            if (board.length == 0 || board[0].length == 0)
                return;
     
            // max row index and max column index
            int maxR = board.length - 1;
            int maxC = board[0].length - 1;
     
            List<Item> list = new ArrayList<Item>();
            int cursor = 0;
            Set<Item> set = new HashSet<Item>();
     
            // left/right edge
            for (int i = 0; i <= maxR; i++) {
                if (board[i][0] == 'O') {
                    Item item = new Item(i, 0, Item.LEFT);
                    if (!set.contains(item)) {
                        list.add(item);
                        set.add(item);
                    }
                }
                if (board[i][maxC] == 'O') {
                    Item item = new Item(i, maxC, Item.RIGHT);
                    if (!set.contains(item)) {
                        list.add(item);
                        set.add(item);
                    }
                }
            }
     
            // top/bottom
            for (int j = 0; j <= maxC; j++) {
                if (board[0][j] == 'O') {
                    Item item = new Item(0, j, Item.UP);
                    if (!set.contains(item)) {
                        list.add(item);
                        set.add(item);
                    }
                }
                if (board[maxR][j] == 'O') {
                    Item item = new Item(maxR, j, Item.DOWN);
                    if (!set.contains(item)) {
                        list.add(item);
                        set.add(item);
                    }
                }
            }
     
            while (cursor < list.size()) {
                Item item = list.get(cursor);
                cursor++;
                int i = item.i;
                int j = item.j;
     
                // up
                if (item.from != Item.UP && i > 0 && board[i - 1][j] == 'O') {
                    Item newItem = new Item(i - 1, j, Item.DOWN);
                    if (!set.contains(newItem)) {
                        list.add(newItem);
                        set.add(newItem);
                    }
                }
     
                // left
                if (item.from != Item.LEFT && j > 0 && board[i][j - 1] == 'O') {
                    Item newItem = new Item(i, j - 1, Item.RIGHT);
                    if (!set.contains(newItem)) {
                        list.add(newItem);
                        set.add(newItem);
                    }
                }
     
                // down
                if (item.from != Item.DOWN && i < maxR && board[i + 1][j] == 'O') {
                    Item newItem = new Item(i + 1, j, Item.UP);
                    if (!set.contains(newItem)) {
                        list.add(newItem);
                        set.add(newItem);
                    }
                }
     
                // right
                if (item.from != Item.RIGHT && j < maxC && board[i][j + 1] == 'O') {
                    Item newItem = new Item(i, j + 1, Item.LEFT);
                    if (!set.contains(newItem)) {
                        list.add(newItem);
                        set.add(newItem);
                    }
                }
            }
     
            // flip
            for (int i = 0; i <= maxR; i++) {
                for (int j = 0; j <= maxC; j++) {
                    Item item = new Item(i, j, 0);
                    if (!set.contains(item))
                        board[i][j] = 'X';
                }
            }
        }
     
    }

    【2014-11-16】更新:其实这个List和这个Set都是可以免去的,对于状态的标记,只需要在原有的board数组上操作就可以了,对于’X’无变化,对于需要flip的’O’,直接翻转,对于不需要翻转的’O’,可以暂时置为’N’标记,等遍历完成以后,最后再把’N’统一回置为’O’。

    LeetCode题目解答——Medium部分(下)

    [Updated on 9/22/2017] 如今回头看来,里面很多做法都不是最佳的,有的从复杂度上根本就不是最优解,有的写的太啰嗦,有的则用了一些过于tricky的方法。我没有为了这个再更新,就让它们去吧。

    这是LeetCode题目Medium难度部分中的下半部分,表格中的Acceptance是LeetCode网上拷贝下来的的数据。这些完成以后,就只剩Hard部分了。欢迎讨论。

    Multiply Strings 20.5% Medium
    Sum Root to Leaf Numbers 29.7% Medium
    Subsets II 27.0% Medium
    Next Permutation 25.4% Medium
    3Sum Closest 27.0% Medium
    Palindrome Partitioning 25.9% Medium
    Subsets 27.9% Medium
    Partition List 27.0% Medium
    Construct Binary Tree from Inorder and Postorder Traversal 26.6% Medium
    Construct Binary Tree from Preorder and Inorder Traversal 26.5% Medium
    Combinations 30.0% Medium
    Combination Sum II 24.7% Medium
    Path Sum II 26.9% Medium
    Permutation Sequence 22.3% Medium
    Permutations 31.2% Medium
    Sqrt(x) 22.3% Medium
    Combination Sum 26.8% Medium
    Populating Next Right Pointers in Each Node 35.3% Medium
    Spiral Matrix II 30.8% Medium
    Pow(x, n) 25.9% Medium
    Spiral Matrix 20.6% Medium
    Sort List 20.6% Medium
    Clone Graph 23.0% Medium
    Remove Duplicates from Sorted Array II 30.6% Medium
    Sort Colors 32.1% Medium
    Remove Duplicates from Sorted List II 24.8% Medium
    Binary Tree Zigzag Level Order Traversal 26.5% Medium
    Binary Tree Preorder Traversal 35.5% Medium
    Reorder List 20.4% Medium
    Restore IP Addresses 20.5% Medium
    Single Number II 33.8% Medium
    Reverse Linked List II 26.1% Medium
    Single Number 45.6% Medium
    Reverse Words in a String 14.0% Medium
    Simplify Path 19.9% Medium
    Rotate Image 31.2% Medium
    Rotate List 22.0% Medium
    Binary Tree Inorder Traversal 35.5% Medium
    Set Matrix Zeroes 30.8% Medium
    Search a 2D Matrix 31.2% Medium
    Search for a Range 27.4% Medium
    Search Insert Position 34.9% Medium
    Search in Rotated Sorted Array II 30.9% Medium

    Multiply Strings

    【题目】Given two numbers represented as strings, return multiplication of the numbers as a string.

    Note: The numbers can be arbitrarily large and are non-negative.

    【解答】按照自己在草稿上计算多位数乘以多位数的的方法那样计算,这种题目要一次性做对还是有难度的。考虑好进位,还有为num1或者num2为零的情况。对了,这里面涉及到反复给字符串的前面添加字符(prepend),如果是append的话我可以用StringBuilder提高效率,但是prepend的话,我找不到一个好的办法(StringBuilder的insert(0, “xxx”)性能上看和普通的字符串拼接一样不可取,都是新建字符串然后System.arrayCopy()的)。如果你知道请告诉我。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    public class Solution {
        public String multiply(String num1, String num2) {
            if ("0".equals(num1) || "0".equals(num2))
                return "0";
     
            String res = "";
            for (int i=num1.length()-1; i>=0; i--) {
                String sum = "";
                int carry = 0;
                int n1 = num1.charAt(i)-'0';
     
                for (int j=num2.length()-1; j>=0; j--) {
                    int n2 = num2.charAt(j)-'0';
     
                    int product = n1*n2 + carry;
                    String digit = product%10 + "";
     
                    carry = product/10;
     
                    sum = digit + sum;
                }
     
                if (carry>0)
                    sum = carry + sum;
     
                res = add(res, sum, (num1.length()-1)-i);
            }
     
            return res;
        }
     
        private String add(String total, String num, int pos) {
            int carry = 0;
            String res = "";
            for (int i=total.length()-1, j=num.length()-1 + pos; i>=0 || j>=0; i--, j--) {
                int n1 = 0;
                if (i>=0)
                    n1 = total.charAt(i) - '0';
     
                int n2 = 0;
                if (j>=0 && j<num.length())
                    n2 = num.charAt(j) - '0';
     
                int sum = n1+n2+carry;
                res = (sum%10) + res;
                carry = sum/10;
            }
     
            if (carry>0)
                res = carry + res;
     
            return res;
        }
    }

    Sum Root to Leaf Numbers

    【题目】Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number.

    An example is the root-to-leaf path 1->2->3 which represents the number 123.

    Find the total sum of all root-to-leaf numbers.

    For example,

        1
       / 
      2   3

    The root-to-leaf path 1->2 represents the number 12.

    The root-to-leaf path 1->3 represents the number 13.

    Return the sum = 12 + 13 = 25.

    【解答】没什么可说的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public class Solution {
        public int sumNumbers(TreeNode root) {
            if (null==root)
                return 0;
     
            List<String> list = getPathSum(root);
            int sum = 0;
            for (String num : list) {
                sum += Integer.parseInt(num);
            }
     
            return sum;
        }
     
        private List<String> getPathSum(TreeNode root) {
            List<String> list = new ArrayList<>();
     
            if (root.left==null && root.right==null)
                list.add(root.val+"");
     
            if (root.left!=null)
                for (String num : getPathSum(root.left)) {
                    list.add(root.val + num);
                }
     
            if (root.right!=null)
                for (String num : getPathSum(root.right)) {
                    list.add(root.val + num);
                }
     
            return list;
        }
    }

    Subsets II

    【题目】Given a collection of integers that might contain duplicates, S, return all possible subsets.

    Note:

    • Elements in a subset must be in non-descending order.
    • The solution set must not contain duplicate subsets.

    For example,
    If S = [1,2,2], a solution is:

    [
      [2],
      [1],
      [1,2,2],
      [2,2],
      [1,2],
      []
    ]

    【解答】和Subsets那道题差不多,唯一的区别是源数组有可能有重复的数字,我在那道题的基础上增加了这样的逻辑:比如num为[1,2,2,2],在某一时刻,list里面为[ [], [1], [2], [1, 2], [1, 2, 2]]的时候,再clone并append后面一个2进list的时候,需要找前面连续的2出现次数最多的那个,即[1,2,2],而其它元素不变。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    public class Solution {
        public List<List<Integer>> subsetsWithDup(int[] num) {
            Arrays.sort(num);
     
            ArrayList<List<Integer>> list = new ArrayList<>();
            List<Integer> l1= new ArrayList<>();
            list.add(l1);
     
            int recent = Integer.MAX_VALUE;
            int dupCount = 0;
            for (int s : num) {
     
                ArrayList<List<Integer>> newList = new ArrayList<>();
                if (recent==s) {
                    for (List<Integer> l : list) {
                        int count = 0;
                        for (int i=l.size()-1; i>=0; i--) {
                            if (l.get(i)==s) {
                                count++;
     
                                if (count==dupCount)
                                    newList.add(new ArrayList<Integer>(l));
                            }
                            else {
                                break;
                            }
                        }
                    }
     
                    dupCount++;
                }
                else {
                    // don't use list.clone()
                    for (List<Integer> l : list) {
                        newList.add(new ArrayList<Integer>(l));
                    }
     
                    recent = s;
                    dupCount = 1;
                }
     
                for (List<Integer> l : newList) {
                    l.add(s);
                }
     
                list.addAll(newList);
            }
     
            return list;
        }
    }

    Next Permutation

    【题目】Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.

    If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).

    The replacement must be in-place, do not allocate extra memory.

    Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.
    1,2,3 → 1,3,2
    3,2,1 → 1,2,3
    1,1,5 → 1,5,1

    【解答】这道题题目让我理解了好半天。给你一串数,要找到下一个排列,比这个排列大,并且恰好比这个排列大,就是说对于原排列和新排列没法找到另一个排列插在这二者之间。比如1,2,3,值是123,那么下一个排列就是132,你没法找到一个居于123和132之间的排列。

    题意搞清楚以后,问题就好办了:

    1. 从最右边那一位开始往左寻找,找到的数应该都是依次增大的,如果发现这个递增的规律被打破,这就说明这个数num[a]需要和它当前右侧的数中“比它大的最小数”num[b]交换。以1,4,3,2为例,从右往左找,从2到3,从3到4都是递增的,但是1出现的时候,这个递增关系被打破,于是在1的右边的比1大的数中,寻找一个最小值,在这里是2。
    2. 交换num[a]和num[b],这就变成了2,4,3,1。
    3. 对于位置a右侧的数按照递增的顺序重排列,变成了2,1,3,4。

    注意特殊情况的处理,对于整个排列从右到左是严格递增的情况,比如4,3,2,1,它的下一个排列是从右到左严格递减的序列,1,2,3,4,这一点起初我给忽略了,题目里面其实有写:“it must rearrange it as the lowest possible order (ie, sorted in ascending order)”。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    public class Solution {
        public void nextPermutation(int[] num) {
     
            for (int i=num.length-1; i>0; i--) {
                if (num[i-1]<num[i]) {
     
                    // find out the minimum number which is larger than num[i-1]
                    int min = Integer.MAX_VALUE;
                    int pos = -1;
     
                    for (int j=i; j<num.length; j++) {
                        if (num[i-1]<num[j] && num[j]<min) {
                            min = num[j];
                            pos = j;
                        }
                    }
     
                    // exchange
                    num[i-1] = num[i-1]^num[pos];
                    num[pos] = num[i-1]^num[pos];
                    num[i-1] = num[i-1]^num[pos];
     
                    // sort, make the sub array [i, num.length) increasing
                    Arrays.sort(num, i, num.length);
     
                    return;
                }
            }
     
            Arrays.sort(num);
            return;
     
        }
    }

    3Sum Closest

    【题目】Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution.

        For example, given array S = {-1 2 1 -4}, and target = 1.
    
        The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).

    【解答】先排序,然后左右各一指针,在sum小于目标值的时候左指针向右走,在sum大于目标值的时候右指针向左走。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    public class Solution {
        public int threeSumClosest(int[] num, int target) {
            Arrays.sort(num);
     
            int closest = java.lang.Integer.MAX_VALUE;
            int sumWhenClosest = 0;
            // i, j, k, when i is determined, the sum = f(j,k)
            for (int i=0; i<num.length; i++) {
                // j traverses from left to right, k traverses from right to left
                int j=i+1;
                int k=num.length-1;
     
                // j can only move towards right and k can only move towards left
                while (j<k){
                    int sum = num[i] + num[j] + num[k];
                    int newClosest = java.lang.Math.abs(sum-target);
                    if (newClosest<closest) {
                        closest = newClosest;
                        sumWhenClosest = sum;
                    }
     
                    if (sum==target)
                        return target;
     
                    if (sum<target)
                        k--;
                    else
                        j++;
                } // while
            } // for
     
            return sumWhenClosest;
        }
    }

    Palindrome Partitioning

    【题目】

    Given a string s, partition s such that every substring of the partition is a palindrome.

    Return all possible palindrome partitioning of s.

    For example, given s = "aab",
    Return

      [
        ["aa","b"],
        ["a","a","b"]
      ]

    【解答】给定一字符串,寻找各种回文串的组合。大致思路是,对于字符串的任一子串(给定了start和end位置,子串即为[start,end)区间的串),对于i∈(start,i],判定[start,i)区间的子串是否为回文,如果是回文,那么递归寻找[i,end)的所有组合可能。写完以后,我觉得如果执行的时候超时,还可以优化成DP,用一张二维表记录中间结果以避免重复计算,不过那是用空间换时间了,既然已经AC就不再优化。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    public class Solution {
        public List<List<String>> partition(String s) {
            List<List<String>> list = new ArrayList<>();
            if (null==s||"".equals(s))
                return list;
     
            return partition(s, 0, s.length());
        }
     
        private List<List<String>> partition(String s, int start, int end) {
            List<List<String>> list = new ArrayList<>();
     
            if (isPalindrome(s, start, end)) {
                List<String> l = new ArrayList<>();
                l.add(s.substring(start, end));
     
                list.add(l);
            }
     
            if (end-start==1) {
                return list;
            }
     
            for (int i=start+1; i<end; i++) {
                if (isPalindrome(s, start, i)) {
                    List<List<String>> ls = partition(s, i, end);
     
                    for (List<String> l : ls) {
                        l.add(0, s.substring(start, i));
                    }
     
                    list.addAll(ls);
                }
            }
     
            return list;
        }
     
        private boolean isPalindrome(String s, int start, int end) {
            for (int i=start, j=end-1; j>i; j--,i++) {
                if (s.charAt(i)!=s.charAt(j))
                    return false;
            }
     
            return true;
        }
    }

    Subsets

    【题目】Given a set of distinct integers, S, return all possible subsets.

    Note:

    • Elements in a subset must be in non-descending order.
    • The solution set must not contain duplicate subsets.

    For example,
    If S = [1,2,3], a solution is:

    [
      [3],
      [1],
      [2],
      [1,2,3],
      [1,3],
      [2,3],
      [1,2],
      []
    ]

    【解答】题目应该说是很简单的,但是我第一遍用递归做,提示超时,第二遍我用循环做,还是超时。我就觉得奇怪,后来发现,在创建新list的时候,不要使用ArrayList.clone()方法,而是循环并调用参数为Collection的构造器:public ArrayList(Collection<? extends E> paramCollection)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public class Solution {
        public List<List<Integer>> subsets(int[] S) {
            Arrays.sort(S);
     
            ArrayList<List<Integer>> list = new ArrayList<>();
            List<Integer> l1= new ArrayList<>();
            list.add(l1);
     
            for (int s : S) {
     
                // don't call list.clone()
                ArrayList<List<Integer>> newList = new ArrayList<>();
                for (List<Integer> l : list) {
                    newList.add(new ArrayList<Integer>(l));
                }
     
                for (List<Integer> l : newList) {
                    l.add(s);
                }
     
                list.addAll(newList);
            }
     
            return list;
        }
    }

    Partition List

    【题目】Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.

    You should preserve the original relative order of the nodes in each of the two partitions.

    For example,
    Given 1->4->3->2->5->2 and x = 3,
    return 1->2->2->4->3->5.

    【解答】看清楚题意,是要把比x小的数搬到不小于x的数的左边去。两个指针,left.next为要搬过去的位置,right.next为被搬的数。增加一个守卫节点fakeHead,这样就可以比较容易地处理要考虑的位置出现在头部的特殊情况。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public class Solution {
        public ListNode partition(ListNode head, int x) {
            ListNode fakeHead = new ListNode(Integer.MIN_VALUE);
            fakeHead.next = head;
     
            ListNode left = fakeHead;
            while (left.next!=null && left.next.val<x)
                left = left.next;
     
            ListNode right = left.next;
            while (right!=null && right.next!=null) {
                if (right.next.val<x) {
                    move(left, right);
                    left = left.next;
                }
                else {
                    right = right.next;
                }
            }
     
            return fakeHead.next;
        }
     
        /**
         * move right.next to left.next
         */
        private void move(ListNode left, ListNode right) {
            ListNode target = right.next;
            right.next = target.next;
     
            ListNode leftNext = left.next;
            left.next = target;
            target.next = leftNext;
        }
    }

    Construct Binary Tree from Inorder and Postorder Traversal

    【题目】Given inorder and postorder traversal of a tree, construct the binary tree.

    Note:
    You may assume that duplicates do not exist in the tree.

    【解答】初看没啥头绪,不过草稿纸上随便画了一个简单的二叉树:1 / 2 3 / 4 5 6 7,

    它的中序遍历是:4 2 5 1 6 3 7

    它的后序遍历是:4 5 2 6 7 3 1

    可以获知的是,对于这样的情形下,根节点是后序遍历序列的最后一个数——1,既然题中说没有重复的数,那么把这个1放到中序遍历的序列中去找,找到了以后,1的左侧的子序列[4,2,5]和后序遍历相应的前三个数[4,5,2]就可以继续构造左子树,而1的右侧的子序列[6,3,7]和后序遍历的接下去的三个数[6,7,3]可以继续构造右子树。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public class Solution {
        public TreeNode buildTree(int[] inorder, int[] postorder) {
            TreeNode fakeRoot = new TreeNode(0);
            buildTree(inorder, 0, inorder.length-1, postorder, 0, postorder.length-1, fakeRoot, true);
            return fakeRoot.left;
        }
     
        private void buildTree(int[] inorder, int istart, int iend, int[] postorder, int pstart, int pend, TreeNode parent, boolean leftOrRight) {
            if (iend<istart)
                return;
     
            // root == postorder[pend]
            for (int i=istart; i<=iend; i++) {
                if (postorder[pend]==inorder[i]) {
                    TreeNode root = new TreeNode(postorder[pend]);
                    if (leftOrRight)
                        parent.left = root;
                    else
                        parent.right = root;
     
                    buildTree(inorder, istart, i-1, postorder, pstart, pstart + (i-1-istart), root, true);
                    buildTree(inorder, i+1, iend, postorder, pend-1 - (iend-i-1), pend - 1, root, false);
                }
            }
        }
    }

    Construct Binary Tree from Preorder and Inorder Traversal

    【题目】Given preorder and inorder traversal of a tree, construct the binary tree.

    Note:
    You may assume that duplicates do not exist in the tree.

    【解答】思路和前面那道题一致,不罗嗦了。前序遍历数组的第一个元素就是根。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class Solution {
        public TreeNode buildTree(int[] preorder, int[] inorder) {
            return buildTree(preorder, 0, preorder.length-1, inorder, 0, inorder.length-1);
        }
     
        private TreeNode buildTree(int[] preorder, int pstart, int pend, int[] inorder, int istart, int iend) {
            if (pstart>pend)
                return null;
     
            for (int i=istart; i<=iend; i++) {
                if (inorder[i]==preorder[pstart]) {
                    TreeNode root = new TreeNode(inorder[i]);
                    root.left = buildTree(preorder, pstart+1, pstart+1+(i-1-istart), inorder, istart, i-1);
                    root.right = buildTree(preorder, pend-(iend-i-1), pend, inorder, i+1, iend);
     
                    return root;
                }
            }
     
            return null;
        }
    }

    Combinations

    【题目】Given two integers n and k, return all possible combinations of k numbers out of 1 … n.

    For example,
    If n = 4 and k = 2, a solution is:

    [
      [2,4],
      [3,4],
      [2,3],
      [1,2],
      [1,3],
      [1,4],
    ]

    【解答】数学归纳法,对于每一种组合f(n,k),都有两种可能组成(注意递归出口,其实只有一个,即k到0的时候,下面这个写得还是啰嗦了一点):

    • n没有被选中,即f(n-1,k),这种情况下需要n>k;
    • n被选中,即f(n-1,k-1)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public class Solution {
        public List<List<Integer>> combine(int n, int k) {
            if (n==0 || k==0) { // for recursion exit 2
                List<Integer> list = new java.util.ArrayList<>();
                List<List<Integer>> total = new java.util.ArrayList<>();
                total.add(list);
                return total;
            } else if (n==k) { // for recursion exit 1
                List<Integer> list = new java.util.ArrayList<>();
                for (int i=1; i<=n; i++) {
                    list.add(i);
                }
                List<List<Integer>> total = new java.util.ArrayList<>();
                total.add(list);
                return total;
            } else {
                // case 1: last element is not selected => recursion exit 1
                List<List<Integer>> subList1 = combine(n-1, k);
     
                // case 2: last element (n) is seleced, so pick k-1 elements out from n-1 totals => recursion exit 2
                List<List<Integer>> subList2 = combine(n-1, k-1);
                // then add the last element
                for (List<Integer> l : subList2) {
                    l.add(n);
                    subList1.add(l); // merge into subList1
                }
     
                return subList1;
            }
        }
     
    }

    Combination Sum II

    【题目】Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

    Each number in C may only be used once in the combination.

    Note:

    • All numbers (including target) will be positive integers.
    • Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
    • The solution set must not contain duplicate combinations.

    For example, given candidate set 10,1,2,7,6,1,5 and target 8,

    A solution set is: 
    [1, 7] 
    [1, 2, 5] 
    [2, 6] 
    [1, 1, 6]

    【解答】看起来和“Combination Sum”那道题差不多,也是要回溯法,但还是有区别的——主要在于去重(比如[2,2,2]这样的数组,如果每次取一个元素,那么取第0、2个元素和取第0、1个元素分别形成的list都是[2,2],这就造成了重复),每次递归调用在从pos开始的连续相同的元素出现时,只有第一次出现的那个才被保留。另外,还有一个小的优化,在于递归调用com方法传递list参数的时候,我并不是每次都创建一个全新的ArrayList传递,而是重用传入的list,add(num[i])改变list的内容,但是递归调用com方法以后,恢复现场,删掉刚才加进去的num[i]。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    public class Solution {
        public List<List<Integer>> combinationSum2(int[] num, int target) {
            Arrays.sort(num);
     
            List<List<Integer>> total = new ArrayList<>();
            List<Integer> l = new ArrayList<>();
            total.add(l);
     
            return com(total, num, target, 0);
        }
     
        public List<List<Integer>> com(List<List<Integer>> list, int[] num, int target, int pos){
            List<List<Integer>> total = new ArrayList<>();
            if (target==0) {
                for(List<Integer> l : list) {
                    List<Integer> nl = new ArrayList<Integer>(l);
                    total.add(nl);
                }
                return total;
            }
     
            for (int i=pos; i<num.length; i++) {
                if (num[i]>target)
                    break;
     
                if(i==pos || num[i]!=num[i-1]) { // dedup
                    for (List<Integer> l:list)
                        l.add(num[i]);
     
                    total.addAll(com(list, num, target-num[i], i+1));
     
                    for (List<Integer> l:list)
                        l.remove(l.size()-1);
                }
            }
     
            return total;
        }
    }

    Path Sum II

    【题目】Given a binary tree and a sum, find all root-to-leaf paths where each path’s sum equals the given sum.

    For example:
    Given the below binary tree and sum = 22,

                  5
                 / 
                4   8
               /   / 
              11  13  4
             /      / 
            7    2  5   1

    return

    [
       [5,4,11,2],
       [5,8,4,5]
    ]

    【解答】其实也没什么可说的。如果题目里面的数都是正数的话,在遍历的过程当中有一些已经大于目标值的list就可以抛弃了,不需要等到递归的最后再判断。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    public class Solution {
        public List<List<Integer>> pathSum(TreeNode root, int sum) {
     
            List<List<Integer>> ret = new ArrayList<>();
            if (root==null)
                return ret;
     
            List<List<Integer>> list = pathSum(root);
     
            for (List<Integer> l : list) {
                int total = 0;
                for (int num : l) {
                    total += num;
                }
     
                if (total==sum)
                    ret.add(l);
            }
     
            return ret;
        }
     
        private List<List<Integer>> pathSum(TreeNode root) {
            List<List<Integer>> paths = new ArrayList<>();
     
            if (root.left!=null) {
                List<List<Integer>> list = pathSum(root.left);
                for (List<Integer> l : list) {
                    l.add(0, root.val);
                }
                paths.addAll(list);
            }
     
            if (root.right!=null) {
                List<List<Integer>> list = pathSum(root.right);
                for (List<Integer> l : list) {
                    l.add(0, root.val);
                }
                paths.addAll(list);
            }
     
            if (root.left==null && root.right==null) {
                List<Integer> item = new ArrayList<>();
                item.add(root.val);
                paths.add(item);
            }
     
            return paths;
        }
    }

    Permutation Sequence

    【题目】The set [1,2,3,…,n] contains a total of n! unique permutations.

    By listing and labeling all of the permutations in order,
    We get the following sequence (ie, for n = 3):

    1. "123"
    2. "132"
    3. "213"
    4. "231"
    5. "312"
    6. "321"

    Given n and k, return the kth permutation sequence.

    Note: Given n will be between 1 and 9 inclusive.

    【解答】对于n,在某一个permutation的最高位确定的时候,下面n-1位的组合可能是(n-1)!种,所以可以用除法从最高位开始依次确定每一位的数值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    public class Solution {
        public String getPermutation(int n, int k) {
     
            ArrayList<Integer> list = new ArrayList<>();
            int factorial = 1;
            for (int i = 1; i <= n; i++) {
                list.add(i);
                factorial *= i;
            }
     
            // !!!
            k = k-1;
     
            StringBuilder sb = new StringBuilder();
     
            for (int i = n; i > 0; i--) {
                factorial = factorial / i;
                int index = k / factorial;
     
                sb.append(list.get(index));
                list.remove(index);
     
                k = k % factorial;
            }
     
            return sb.toString();
        }
    }

    Permutations

    【题目】Given a collection of numbers, return all possible permutations.

    For example,
    [1,2,3] have the following permutations:
    [1,2,3][1,3,2][2,1,3][2,3,1][3,1,2], and [3,2,1].

    【解答】这题似乎没啥大意思,就是循环遍历到所有情况就行了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    public class Solution {
        public List<List<Integer>> permute(int[] num) {
            List<Integer> src = new ArrayList<>();
            for (int i=0; i<num.length; i++) {
                src.add(num[i]);
            }
     
            List<Integer> l = new ArrayList<>();
            List<List<Integer>> list = new ArrayList<>();
            list.add(l);
     
            return permute(list, src);
        }
     
        private List<List<Integer>> permute(List<List<Integer>> list, List<Integer> src) {
            if (src.isEmpty())
                return list;
     
            List<List<Integer>> total = new ArrayList<>();
     
            for (int i=0; i<src.size(); i++) {
                int num = src.get(i);
                List<Integer> newSrc = new ArrayList<>(src);
                newSrc.remove(i);
                List<List<Integer>> newList = new ArrayList<>();
                for (List<Integer> l : list) {
                    List<Integer> copy = new ArrayList<Integer>(l);
                    copy.add(num);
                    newList.add(copy);
                }
     
                total.addAll(permute(newList, newSrc));
            }
     
            return total;
        }
    }

    Sqrt(x)

    【题目】Implement int sqrt(int x).

    Compute and return the square root of x.

    【解答】基本上是根据二分搜索的办法求解,但是有这么两个需要注意的地方:

    • 使用long是因为考虑可能溢出的情况;
    • 这题有点恶心的地方在于,要求返回int型,比如当x是2的时候,要求返回1,所以while (min <= max)这行就是起这个作用的,当min==max的时候,并不意味着找到这个数了,如果不符合条件(比如x=2的时候,第二遍循环后min会变成2,max会变成1),这时候离开了while,必须返回max。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class Solution {
        public int sqrt(int x) {
            long min = 0;
            long max = x / 2 + 1;
     
            while (min <= max)
            {
                long mid = (min + max) / 2;
                long product = mid * mid;
     
                if (product == x)
                    return (int)mid;
                else if (product < x)
                    min = mid + 1;
                else
                    max = mid - 1;
            }
     
            return (int)max;
        }
    }

    后来知道有种“牛顿迭代法”可以用来比二分查找更高效地求解平方根。

    Combination Sum

    【题目】Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

    The same repeated number may be chosen from C unlimited number of times.

    Note:

    • All numbers (including target) will be positive integers.
    • Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
    • The solution set must not contain duplicate combinations.

    For example, given candidate set 2,3,6,7 and target 7,

    A solution set is: 
    [7] 
    [2, 2, 3]

    【解答】回溯法求解。先给candidates排序,然后每次递归假设取candidates在pos位置上的数会取i个,然后在从[pos+1,candidates.length)这个区间里面取剩下的数,并且它们的和为target-i*candidaates[pos]。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public class Solution {
        public List<List<Integer>> combinationSum(int[] candidates, int target) {
            Arrays.sort(candidates);
     
            List<List<Integer>> list = new ArrayList<>();
            List<Integer> l = new ArrayList<>();
            list.add(l);
     
            return combinationSum(list, candidates, target, 0);
        }
     
        private List<List<Integer>> combinationSum(List<List<Integer>> list, int[] candidates, int target, int pos) {
            List<List<Integer>> total = new ArrayList<>();
            if (pos==candidates.length) {
                if (target==0)
                    return list;
                return total;
            }
     
            for (int i=0; i*candidates[pos]<=target; i++) {
                List<List<Integer>> newList = new ArrayList<>();
                for (List<Integer>l : list) {
                    List<Integer> nl = new ArrayList<Integer>(l);
                    for (int j=1; j<=i; j++)
                        nl.add(candidates[pos]);
                    newList.add(nl);
                }
                total.addAll(combinationSum(newList, candidates, target-i*candidates[pos], pos+1));
            }
     
            return total;
        }
    }

    Populating Next Right Pointers in Each Node

    【题目】Given a binary tree

        struct TreeLinkNode {
          TreeLinkNode *left;
          TreeLinkNode *right;
          TreeLinkNode *next;
        }

    Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL.

    Initially, all next pointers are set to NULL.

    Note:

    • You may only use constant extra space.
    • You may assume that it is a perfect binary tree (ie, all leaves are at the same level, and every parent has two children).

    For example,

    Given the following perfect binary tree,

             1
           /  
          2    3
         /   / 
        4  5  6  7

    After calling your function, the tree should look like:

             1 -> NULL
           /  
          2 -> 3 -> NULL
         /   / 
        4->5->6->7 -> NULL

    【解答】一层一层往下建立next关系:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class Solution {
        public void connect(TreeLinkNode root) {
            traverse (root, null);
        }
     
        public void traverse (TreeLinkNode node, TreeLinkNode next) {
            if(null==node)
                return;
     
            node.next = next;
     
            // left child
            traverse (node.left, node.right);
     
            // right child
            if (null==node.next)
                traverse (node.right, null);
            else
                traverse (node.right, node.next.left);
        }
    }

    Spiral Matrix II

    【题目】Given an integer n, generate a square matrix filled with elements from 1 to n2 in spiral order.

    For example,
    Given n = 3,

    You should return the following matrix:

    [
     [ 1, 2, 3 ],
     [ 8, 9, 4 ],
     [ 7, 6, 5 ]
    ]

    【解答】这道题和“Spiral Matrix”本质上没啥区别,思路也是几乎一模一样的,不提了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    public class Solution {
        private static final int UP = 1;
        private static final int DOWN = 2;
        private static final int LEFT = 3;
        private static final int RIGHT = 4;
     
        public int[][] generateMatrix(int n) {
            int[][] matrix = new int[n][n];
            if (matrix.length==0 || matrix[0].length==0)
                return matrix;
     
            int direction = RIGHT;
     
            int boundUp = 0;
            int boundDown = matrix.length;
            int boundLeft = -1;
            int boundRight = matrix[0].length;
     
            int x=0;
            int y=0;
     
            int i=1;
     
            while (boundUp<boundDown && boundLeft<boundRight) {
                matrix[y][x] = i++;
     
                switch (direction) {
                    case RIGHT:
                        if (x<boundRight-1) {
                            x++;
                        } else if (y<boundDown-1) {
                            y++;
                            direction = DOWN;
                            boundRight--;
                        } else {
                            return matrix;
                        }
                        break;
                    case DOWN:
                        if (y<boundDown-1) {
                            y++;
                        } else if (x>boundLeft+1) {
                            x--;
                            direction = LEFT;
                            boundDown--;
                        } else {
                            return matrix;
                        }
                        break;
                    case LEFT:
                        if (x>boundLeft+1) {
                            x--;
                        } else if (y>boundUp+1) {
                            y--;
                            direction = UP;
                            boundLeft++;
                        } else {
                            return matrix;
                        }
                        break;
                    case UP:
                        if (y>boundUp+1) {
                            y--;
                        } else if (x<boundRight-1) {
                            x++;
                            direction = RIGHT;
                            boundUp++;
                        } else {
                            return matrix;
                        }
                        break;
                }
            }
     
            return matrix;
        }
    }

    Pow(x, n)

    【题目】Implement pow(xn).

    【解答】初看起来挺简单的题目,但是有陷阱,要一遍做对其实挺不容易的:

    • 最开始我考虑用一个数组来存放以往计算过的记录,但是对于临界的case,数组下标达到了int型的上限,而且实际上这个数组里面大部分元素都是空的,利用率很低,于是改成HashMap;
    • 为了避免溢出(溢出的原因是当n为Integer.MIN_VALUE的时候,它是没法取相反值的,一取就溢出了),用long型来作为key,而不是int;
    • 需要考虑n为负数的情况。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public class Solution {
        public double pow(double x, int n) {
            long m = (long)n;
            boolean negative = false;
            if (m<=0) {
                m = -m;
                negative = true;
            }
            Map<Long, Double> map = new HashMap<>();
            double ret = pow(x, m, map);
     
            if (negative)
                return 1/ret;
     
            return ret;
        }
     
        private double pow(double x, long n, Map<Long, Double> map) {
            if (n==0)
                return 1;
            if (n==1)
                return x;
     
            if (map.containsKey(n))
                return map.get(n);
     
            double ret = pow(x, n/2, map) * pow(x, (n-n/2), map);
            map.put(n, ret);
     
            return ret;
        }
    }

    Spiral Matrix

    【题目】Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order.

    For example,
    Given the following matrix:

    [
     [ 1, 2, 3 ],
     [ 4, 5, 6 ],
     [ 7, 8, 9 ]
    ]

    You should return [1,2,3,6,9,8,7,4,5].

    【解答】就是状态机,根据每走一步前的状态决定下一步应该怎么走。值得小心的是,如果按照数组的行列习惯matrix[x][y]表示的是第x行,第y列;如果按照坐标轴的习惯,x轴是横轴,表示的是第几列,y轴是纵轴,表示的是第几行,所以x行y列应该表示为matrix[y][x]。选择其中一种,别搞乱了就好。这里我选择了后者。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    public class Solution {
        private static final int UP = 1;
        private static final int DOWN = 2;
        private static final int LEFT = 3;
        private static final int RIGHT = 4;
     
        public List<Integer> spiralOrder(int[][] matrix) {
            List<Integer> list = new ArrayList<>();
            if (matrix.length==0 || matrix[0].length==0)
                return list;
     
            int direction = RIGHT;
     
            int boundUp = 0;
            int boundDown = matrix.length;
            int boundLeft = -1;
            int boundRight = matrix[0].length;
     
            int x=0;
            int y=0;
     
            while (boundUp<boundDown && boundLeft<boundRight) {
                list.add(matrix[y][x]);
     
                switch (direction) {
                    case RIGHT:
                        if (x<boundRight-1) {
                            x++;
                        } else if (y<boundDown-1) {
                            y++;
                            direction = DOWN;
                            boundRight--;
                        } else {
                            return list;
                        }
                        break;
                    case DOWN:
                        if (y<boundDown-1) {
                            y++;
                        } else if (x>boundLeft+1) {
                            x--;
                            direction = LEFT;
                            boundDown--;
                        } else {
                            return list;
                        }
                        break;
                    case LEFT:
                        if (x>boundLeft+1) {
                            x--;
                        } else if (y>boundUp+1) {
                            y--;
                            direction = UP;
                            boundLeft++;
                        } else {
                            return list;
                        }
                        break;
                    case UP:
                        if (y>boundUp+1) {
                            y--;
                        } else if (x<boundRight-1) {
                            x++;
                            direction = RIGHT;
                            boundUp++;
                        } else {
                            return list;
                        }
                        break;
                }
            }
     
            return list;
        }
    }

    Sort List

    【题目】Sort a linked list in O(n log n) time using constant space complexity.

    【解答】要n*logn复杂度的排序,我最开始尝试用快排去解,但是很快遇到问题,原因在于这是个linked list,快排经常需要寻找一个节点的前一个节点,不是不能做,而是很麻烦,自己做做就烦了,干脆换成归并排序,这就很容易完成了,因为归并排序不需要往回走。不过,归并排序需要不断地寻找中间切分链表的节点,利用快慢双指针来寻找这样的一个节点。另外,在归并的时候,建立一个fake head,让代码简单一些,不需要处理头部的特殊情况。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    public class Solution {
        public ListNode sortList(ListNode head) {
            if (head==null || head.next==null)
                return head;
     
            ListNode fast = head, slow = head;
            while (fast.next!=null && fast.next.next!=null)
            {
                fast = fast.next.next; // 2 steps
                slow = slow.next; // 1 step
            }
     
            // split
            fast = slow;
            slow = slow.next;
            fast.next = null;
     
            fast = sortList(head);
            slow = sortList(slow);
     
            return merge(fast, slow);
        }
     
        private ListNode merge(ListNode h1, ListNode h2) {
            if(h1==null)
                return h2;
            if(h2==null)
                return h1;
     
            // fake head
            ListNode head = new ListNode(0);
            ListNode node = head;
     
            while (h1!=null && h2!=null)
            {
                if(h1.val < h2.val)
                {
                    node.next = h1;
                    h1 = h1.next;
                } else {
                    node.next = h2;
                    h2 = h2.next;
                }
                node = node.next;
            }
     
            if (h1!=null)
                node.next = h1;
            else if (h2!=null)
                node.next = h2;
     
            return head.next;
        }
    }

    Clone Graph

    【题目】

    Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors.


    OJ’s undirected graph serialization:

    Nodes are labeled uniquely.

    We use # as a separator for each node, and , as a separator for node label and each neighbor of the node.

    As an example, consider the serialized graph {0,1,2#1,2#2,2}.

    The graph has a total of three nodes, and therefore contains three parts as separated by #.

    1. First node is labeled as 0. Connect node 0 to both nodes 1 and 2.
    2. Second node is labeled as 1. Connect node 1 to node 2.
    3. Third node is labeled as 2. Connect node 2 to node 2 (itself), thus forming a self-cycle.

    Visually, the graph looks like the following:

           1
          / 
         /   
        0 --- 2
             / 
             \_/

    【解答】这是一种存在环的图,我的办法是把整个图的拷贝拆成两步,第一步创建节点,第二步才是建立联系。为了避免递归导致栈溢出,要严格避免在递归传递的过程中出现中间状态,比如说,buildRelation那一步,在所有的关联关系给当前节点添加完成之前,不可以执行递归调用的逻辑。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    public class Solution {
        public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
            if (null==node)
                return null;
     
            Map<Integer, UndirectedGraphNode> map = new HashMap<>();
            buildNodes(node, map);
            buildRelation(node, map);
     
            return map.get(node.label);
        }
     
        private void buildNodes(UndirectedGraphNode node, Map<Integer, UndirectedGraphNode> map) {
            if (!map.containsKey(node.label)) {
                UndirectedGraphNode n = new UndirectedGraphNode(node.label);
                map.put(node.label, n);
            }
     
            for (UndirectedGraphNode ne : node.neighbors) {
                if (!map.containsKey(ne.label)) {
                    map.put(ne.label, new UndirectedGraphNode(ne.label));
                    buildNodes(ne, map);
                }
            }
        }
     
        private void buildRelation(UndirectedGraphNode node, Map<Integer, UndirectedGraphNode> map) {
            UndirectedGraphNode n = map.get(node.label);
     
            if(node.neighbors.size()!=n.neighbors.size()) {
                for (UndirectedGraphNode ne : node.neighbors) {
                    n.neighbors.add(map.get(ne.label));
                }
     
                for (UndirectedGraphNode ne : node.neighbors) {
                    buildRelation(ne, map);
                }
            }
     
        }
    }

    Remove Duplicates from Sorted Array II

    【题目】Follow up for “Remove Duplicates”:

    What if duplicates are allowed at most twice?

    For example,
    Given sorted array A = [1,1,1,2,2,3],

    Your function should return length = 5, and A is now [1,1,2,2,3].

    【解答】如下。不过我的代码arraycopy拷贝的总元素数目多,这一步其实可以优化,使得所有元素至多只被拷贝一次。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    public class Solution {
        public int removeDuplicates(int[] A) {
            if (A.length<3)
                return A.length;
     
            int slow = 0;
            int fast = 1;
     
            int dup = 1;
            int len = A.length;
     
            while (fast<len) {
                if (A[slow]==A[fast]) {
                    dup++;
                    fast++;
                } else {
                    if (dup>2) {
                        System.arraycopy(A, fast, A, slow+2, len-fast);
                        len = len - (fast-slow) + 2;
     
                        slow = slow + 1;
                        fast = slow + 1;
                    } else {
                        slow = fast;
                        fast++;
                    }
     
                    dup = 1;
                }
            }
     
            if (dup>2) {
                len = len - (fast-slow) + 2;
            }
     
            return len;
        }
    }

    Sort Colors

    【题目】Given an array with n objects colored red, white or blue, sort them so that objects of the same color are adjacent, with the colors in the order red, white and blue.

    Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively.

    Note:
    You are not suppose to use the library’s sort function for this problem.

    【解答】两个指针,先交换红色的,再交换白色的,只需要常数的额外空间。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    public class Solution {
        private static final int RED = 0;
        private static final int WHITE = 1;
        private static final int BLUE = 2;
     
        public void sortColors(int[] A) {
            if (A.length<2)
                return;
     
            int i=0, j=1;
            // RED
            while (j<A.length) {
                if (A[i]!=RED) {
                    if (A[j]==RED) {
                        // swap
                        int temp = A[i];
                        A[i] = A[j];
                        A[j] = temp;
     
                        i++;
                        j++;
                    } else {
                        j++;
                    }
                } else {
                    i++;
                    if (i==j)
                        j++;
                }
            }
     
            // WHITE
            j=i+1;
            while (j<A.length) {
                if (A[i]!=WHITE) {
                    if (A[j]==WHITE) {
                        // swap
                        int temp = A[i];
                        A[i] = A[j];
                        A[j] = temp;
     
                        i++;
                        j++;
                    }
                    else {
                        j++;
                    }
                } else {
                    i++;
                    if (i==j)
                        j++;
                }
            }
     
        }
    }

    Remove Duplicates from Sorted List II

    【题目】Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.

    For example,
    Given 1->2->3->3->4->4->5, return 1->2->5.
    Given 1->1->1->2->3, return 2->3.

    【解答】快慢两个指针,慢指针占据重复子串的前面那个位置,快指针占据重复子串的最后一位,所以每次删掉重复子串都是slow.next = fast.next。注意考虑其中fast.next==null的特殊情况。另外,引入一个fake head可以帮助简化问题。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class Solution {
        public ListNode deleteDuplicates(ListNode head) {
            ListNode fake = new ListNode(Integer.MIN_VALUE);
            fake.next = head;
     
            ListNode slow = fake;
            ListNode fast = fake.next;
     
            while (fast != null) {
                if (slow.next != fast && slow.next.val == fast.val
                        && (fast.next == null || fast.val != fast.next.val)) {
                    slow.next = fast.next;
                    fast = slow.next;
                } else {
                    if (fast.next == null || fast.val != fast.next.val)
                        slow = fast;
                    fast = fast.next;
                }
            }
     
            return fake.next;
        }
    }

    Binary Tree Zigzag Level Order Traversal

    【题目】Given a binary tree, return the zigzag level order traversal of its nodes’ values. (ie, from left to right, then right to left for the next level and alternate between).

    For example:
    Given binary tree {3,9,20,#,#,15,7},

        3
       / 
      9  20
        /  
       15   7

    return its zigzag level order traversal as:

    [
      [3],
      [20,9],
      [15,7]
    ]

    【解答】锯齿形一层一层输出。没什么难度,不过注意容易出错的地方,一个是每一层输出的方向,还有一个是递归出口。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    public class Solution {
        public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
            if (null==root)
                return new ArrayList<List<Integer>>();
     
            List<TreeNode> nodes = new ArrayList<>();
            nodes.add(root);
     
            return traverse(nodes, true);
        }
     
        private List<List<Integer>> traverse(List<TreeNode> nodes, boolean leftToRight) {
     
            List<List<Integer>> total = new ArrayList<>();
            List<Integer> l = new ArrayList<>();
            if (leftToRight) {
                for (TreeNode n : nodes) {
                    l.add(n.val);
                }
            } else {
                for (int i=nodes.size()-1; i>=0; i--) {
                    l.add(nodes.get(i).val);
                }
            }
            total.add(l);
     
            List<TreeNode> ns = new ArrayList<>();
            for (TreeNode n : nodes) {
                if (n.left!=null)
                    ns.add(n.left);
                if (n.right!=null)
                    ns.add(n.right);
            }
            if (!ns.isEmpty())
                total.addAll(traverse(ns, !leftToRight));
     
            return total;
        }
    }

    Binary Tree Preorder Traversal

    【题目】Given a binary tree, return the preorder traversal of its nodes’ values.

    For example:
    Given binary tree {1,#,2,3},

       1
        
         2
        /
       3

    return [1,2,3].

    Note: Recursive solution is trivial, could you do it iteratively?

    【解答】无论是先序、中序还是后序遍历,如果是递归,都很简单:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class Solution {
        private List<Integer> array = new java.util.ArrayList<Integer>();
        public List<Integer> preorderTraversal(TreeNode root) {
            traverse(root);
            return array;
        }
     
        private void traverse(TreeNode node){
            if(node==null)
                return;
     
            array.add(node.val);
            traverse(node.left);
            traverse(node.right);
        }
    }

    但是如果是循环,那么先序和中序遍历也比较简单(后序遍历就会比较难):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class Solution {
        public List<Integer> preorderTraversal(TreeNode root) {
            List<Integer> list = new ArrayList<>();
            if (root==null)
                return list;
     
            Stack<TreeNode> stack = new Stack<>();
            stack.push(root);
     
            while (!stack.isEmpty()) {
                TreeNode node = stack.pop();
                list.add(node.val);
     
                if (node.right!=null)
                    stack.push(node.right);
     
                if (node.left!=null)
                    stack.push(node.left);
            }
     
            return list;
        }
    }

    Reorder List

    【题目】Given a singly linked list LL0→L1→…→Ln-1→Ln,

    reorder it to: L0→LnL1→Ln-1→L2→Ln-2→…

    You must do this in-place without altering the nodes’ values.

    For example,
    Given {1,2,3,4}, reorder it to {1,4,2,3}.

    【解答】先用一个stack存放从后往前的节点。然后一条路线是从原链表的head开始往后遍历,每次元素为cur,另一条路线是从stack里面pop出元素n,每次每条路线各取一个节点放到一起。注意处理特殊情形,在链表结点为奇数个时,会出现cur==n;在链表节点为偶数时,当次迭代执行完毕后cur.next==n。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    public class Solution {
        public void reorderList(ListNode head) {
            Stack<ListNode> stack = new Stack<>();
            ListNode cur = head;
            while (cur!=null) {
                stack.push(cur);
                cur = cur.next;
            }
     
            cur = head;
            while (cur!=null) {
                ListNode n = stack.pop();
                if (cur!=n) {
                    ListNode temp = cur;
                    cur = cur.next;
                    temp.next = n;
                    n.next = cur;
                    if (cur.next==n) {
                        cur.next = null;
                        break;
                    }
                } else {
                    cur.next = null;
                    break;
                }
            }
        }
    }

    Restore IP Addresses

    【题目】Given a string containing only digits, restore it by returning all possible valid IP address combinations.

    For example:
    Given "25525511135",

    return ["255.255.11.135", "255.255.111.35"]. (Order does not matter)

    【解答】回溯法递归求解。这里有一个陷阱,就是像101011这样的case,是不能切分成1.01.0.11这样的,因为第二个块是01,是需要去除的情形。所以下面代码中排除掉subStr以“0”开头但是长度又大于1的分支。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    public class Solution {
        public List<String> restoreIpAddresses(String s) {
            return restore(new int[0], s);
        }
     
        private List<String> restore(int[] tokens, String rest) {
            List<String> total = new ArrayList<>();
            if (tokens.length==4) {
                if ("".equals(rest)) {
                    String ip = "";
                    for (int i=0; i<4; i++) {
                        if (!"".equals(ip))
                            ip += '.';
                        ip += String.valueOf(tokens[i]);
                    }
                    total.add(ip);
                }
     
                return total;
            }
     
            for (int i=1; i<=3 && i<=rest.length(); i++) {
                String subStr = rest.substring(0, i);
                int sub = Integer.parseInt(subStr);
                if ((subStr.startsWith("0") && i>1) || sub>255)
                    break;
     
                int[] ts = new int[tokens.length+1];
                System.arraycopy(tokens, 0, ts, 0, tokens.length);
                ts[tokens.length] = sub;
     
                total.addAll(restore(ts, rest.substring(i)));
            }
     
            return total;
        }
    }

    Single Number II

    【题目】Given an array of integers, every element appears three times except for one. Find that single one.

    Note:
    Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

    【解答】下面这种方法,是利用一个掩码来统计某一位的出现次数,这是一种比较常用的办法,具体的分析,我写过一篇文章

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Solution {
        public int singleNumber(int[] A) {
            int ret = 0;
            for (int i = 0; i < 32; i++)
            {
                int count = 0, mask = 1<<i;
                for (int j = 0; j < A.length; j++)
                    if ( (A[j] & mask) > 0 || (A[j] & mask) < 0 )
                        count++;
     
                // for the most integers the count should be 0 or 3
                if (count%3 > 0)
                    ret |= mask;
            }
            return ret;
        }
    }

    Reverse Linked List II

    【题目】Reverse a linked list from position m to n. Do it in-place and in one-pass.

    For example:
    Given 1->2->3->4->5->NULLm = 2 and n = 4,

    return 1->4->3->2->5->NULL.

    Note:
    Given mn satisfy the following condition:
    1 ≤ m ≤ n ≤ length of list.

    【解答】要做到“in-place”和“one-pass”,大致思路是这样,[m,n]段,先反转节点之间链接指向,然后再把n接到它本来前面的[1,m)段去,而把m接到它本来后面的(n,null]段去。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    public class Solution {
        public ListNode reverseBetween(ListNode head, int m, int n) {
            ListNode fake = new ListNode(0);
            fake.next = head;
     
            // get the mth node
            ListNode mth = fake;
            for (int i = 1; i < m; i++) {
                mth = mth.next;
            }
     
            ListNode cur = mth.next;
            ListNode tail = cur;
            int count = 1;
     
            ListNode prev = null;
            ListNode next = null;
            while (count <= n-m+1) {
                // shift link direction
                next = cur.next;
                cur.next = prev;
                prev = cur;
                cur = next;
     
                count++;
            }
     
            // head/tail
            mth.next = prev;
            tail.next = next;
     
            return fake.next;
        }
    }

    Single Number

    【题目】Given an array of integers, every element appears twice except for one. Find that single one.

    Note:
    Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

    【解答】用异或就好了,一个数异或它自己就为0。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Solution {
        public int singleNumber(int[] A) {
            if(null==A)
                throw new IllegalArgumentException();
            int total = 0;
            for (int a : A)
                total ^= a;
            return total;
        }
    }

    Reverse Words in a String

    【题目】

    Given an input string, reverse the string word by word.

    For example,
    Given s = “the sky is blue“,
    return “blue is sky the“.

    Clarification:
    • What constitutes a word?
      A sequence of non-space characters constitutes a word.
    • Could the input string contain leading or trailing spaces?
      Yes. However, your reversed string should not contain leading or trailing spaces.
    • How about multiple spaces between two words?
      Reduce them to a single space in the reversed string.

    【解答】两个指针,一个指针cur只顾着从右往左走,另一个指针wordEnd始终占据单词后面的那个空格,这样每次cur发现空格的时候substring(cur+1, wordEnd)就是该单词。需要注意的是,题目说得很明确,需要注意leading和trailing的空格要去掉,还有中间连续的空格需要变成一个,所以需要有判断条件wordEnd-1>cur,保证连续空格的情况下不会被写到字符串里面去,只有真正的单词出现的时候才需要append空格。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public class Solution {
        public String reverseWords(String s) {
            int wordEnd = s.length();
            int cur = wordEnd - 1;
     
            StringBuilder sb = new StringBuilder();
     
            while (cur>=0) {
                if (s.charAt(cur)==' ') {
                    if (wordEnd-1>cur) {
                        if (sb.length()>0)
                            sb.append(' ');
                        sb.append(s.substring(cur+1, wordEnd));
                    }
                    wordEnd = cur;
                }
     
                cur--;
            }
     
            if (wordEnd-1>cur) {
                if (sb.length()>0)
                    sb.append(' ');
                sb.append(s.substring(0, wordEnd));
            }
     
            return sb.toString();
        }
    }

    Simplify Path

    【题目】Given an absolute path for a file (Unix-style), simplify it.

    For example,
    path = "/home/", => "/home"
    path = "/a/./b/../../c/", => "/c"

    【解答】这个题目主要是考虑case是不是考虑完备,包括:

    • ///
    • /..
    • a/
    • ./a
    • /…
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    public class Solution {
        public String simplifyPath(String path) {
            String[] tokens = path.split("/");
            String[] parsed = new String[tokens.length];
     
            int pos = 0;
            for (String token : tokens) {
                if ("".equals(token) || ".".equals(token)) {
                    continue;
                } else if ("..".equals(token)) {
                    if (pos>0) {
                        pos--;
                    }
                } else {
                    parsed[pos] = token;
                    pos++;
                }
            }
     
            String init = "";
            if (path.startsWith("/"))
                init = "/";
     
            StringBuilder sb = new StringBuilder(init);
            boolean first = true;
            for (int i=0; i<pos; i++) {
                if (!first)
                    sb.append('/');
                first = false;
     
                sb.append(parsed[i]);
            }
     
            return sb.toString();
        }
    }

    Rotate Image

    【题目】You are given an n x n 2D matrix representing an image.

    Rotate the image by 90 degrees (clockwise).

    Follow up:
    Could you do this in-place?

    【解答】先在草稿纸上画画,就能发现下面代码中注释里面写的那个点的移动规律。也就是说,每次rotate,都需要四个一组来进行操作。两个嵌套的for循环保证了主动发起rotate的单元是正方形的中心和两个相邻顶点的连线以及这两个顶点连线所构成的三角形(下图中的三个红圈是这个三角形的三个顶点)。

    NewImage

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class Solution {
        /**
         * (i,j) => (j,n-i)
         * (j,n-i) => (n-i,n-j)
         * (n-i,n-j) => (n-j,i)
         * (n-j,i) => (i,j)
         **/
        public void rotate(int[][] matrix) {
            int n = matrix.length-1;
     
            for (int i=0; i<=n; i++) {
                for (int j=i+1; j<=n-i; j++) {
                    int n1 = matrix[i][j];
                    int n2 = matrix[j][n-i];
                    int n3 = matrix[n-i][n-j];
                    int n4 = matrix[n-j][i];
     
                    matrix[i][j] = n4;
                    matrix[j][n-i] = n1;
                    matrix[n-i][n-j] = n2;
                    matrix[n-j][i] = n3;
                }
            }
        }
    }

    Rotate List

    【题目】Given a list, rotate the list to the right by k places, where k is non-negative.

    For example:
    Given 1->2->3->4->5->NULL and k = 2,
    return 4->5->1->2->3->NULL.

    【解答】用一个栈存放所有节点,然后出栈k个元素,把出栈的这k个元素放到原head的前面去。注意的是这么两个地方:

    • rotate的次数可能大于这个list元素的个数,因此需要取余;
    • head为空或者栈为空时的处理。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public class Solution {
        public ListNode rotateRight(ListNode head, int n) {
            if (null==head)
                return head;
     
            Stack<ListNode> stack = new Stack<>();
            ListNode node = head;
            while(node!=null) {
                stack.push(node);
                node = node.next;
            }
     
            n = n % stack.size();
            if (n==0)
                return head;
     
            ListNode tail = stack.peek();
            for (int i=n; i>0; i--)
                node = stack.pop();
     
            stack.peek().next = null;
            tail.next = head;
     
            return node;
        }
    }

    Binary Tree Inorder Traversal

    【题目】Given a binary tree, return the inorder traversal of its nodes’ values.

    For example:
    Given binary tree {1,#,2,3},

       1
        
         2
        /
       3

    return [1,3,2].

    Note: Recursive solution is trivial, could you do it iteratively?

    【解答】递归的方式不用说了。非递归的方式,可以参考先序遍历那道题的思路,但是如果按照那个思路来做,在如下情形下是有问题的:

    因为某节点A有左孩子,中序遍历要求先遍历左子树,因此要把A入栈,这没错;但是当把A出栈以后,A节点因为有左孩子,又要入栈, 这就无限循环下去了。

    因此稍微改进一下,凡是从栈中取出的节点,立即访问,并且在访问完毕以后,绝不要放回栈里面,并且让节点指针指向它的右孩子,这样就可以保证访问过的节点不可能再被访问到:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class Solution {
        public List<Integer> inorderTraversal(TreeNode root) {
            List<Integer> list = new ArrayList<>();
     
            Stack<TreeNode> stack = new Stack<>();
            TreeNode node = root;
     
            while (null!=node || !stack.isEmpty()) {
                if (null!=node) {
                    stack.push(node);
                    node = node.left;
                } else {
                    // node is null, but stack is not empty
                    node = stack.pop();
                    if (null==node)
                        return list;
                    list.add(node.val);
     
                    node = node.right;
                }
            }
     
            return list;
        }
    }

    Set Matrix Zeroes

    【题目】Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in place.

    Follow up:

    Did you use extra space?
    A straight forward solution using O(mn) space is probably a bad idea.
    A simple improvement uses O(m + n) space, but still not the best solution.
    Could you devise a constant space solution?

    【解答】先遍历一遍,把需要置为0的行和列都保存下来,再置零。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class Solution {
        public void setZeroes(int[][] matrix) {
            if (matrix.length==0 || matrix[0].length==0)
                return;
            List<Integer> rows = new ArrayList<>();
            List<Integer> cols = new ArrayList<>();
     
            for (int i=0; i<matrix.length; i++) {
                for (int j=0; j<matrix[0].length; j++) {
                    if (matrix[i][j]==0) {
                        rows.add(i);
                        cols.add(j);
                    }
                }
            }
     
            for (int row : rows) {
                for (int j=0; j<matrix[0].length; j++) {
                    matrix[row][j] = 0;
                }
            }
     
            for (int col : cols) {
                for (int i=0; i<matrix.length; i++) {
                    matrix[i]
    [col] = 0;
                }
            }
        }
    }
    1
    2
    3
    4
    5
    题是做出来了,但是这个办法的空间复杂度是O(m+n),按照题中的follow up,应该是还可以继续优化的。比如用位存储是一个节约空间的好办法。当然,我也考虑过看看能不能用常量的空间复杂度,我当时的做法是在第一遍遍历的时候就给需要置为0的行列元素全部标记为一个特殊值,但是对于一个int型的数组,没有一个特殊值可以供我标记。于是我去网上找了别人的解法,发现确实是有办法做到这一点的,其中一个办法就是,先把整个矩阵的第1行和第1列拿来作置零标尺。比如第k行必须置零,那么就把matrix[k-1][0]置为0,到最后在根据标尺上的信息来完成全部的置零操作。
     
    <strong><span id="search-a-2d-matrix" style="color: #ff0000;">Search a 2D Matrix</span></strong>
     
    【题目】<span style="color: #333333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 30px;">Write an efficient algorithm that searches for a value in an </span><em style="box-sizing: border-box;">m</em><span style="color: #333333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 30px;"> x </span><em style="box-sizing: border-box;">n</em><span style="color: #333333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 30px;"> matrix. This matrix has the following properties:</span>
    • Integers in each row are sorted from left to right.
    • The first integer of each row is greater than the last integer of the previous row.
    1
    2
    3
    For example,
     
    Consider the following matrix:
    [
      [1,   3,  5,  7],
      [10, 11, 16, 20],
      [23, 30, 34, 50]
    ]
    1
    2
    3
    Given <span style="box-sizing: border-box; font-weight: bold;">target</span> = <code style="box-sizing: border-box; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; font-size: 13px; padding: 2px 4px; color: #c7254e; border-radius: 4px; ">3</code>, return <code style="box-sizing: border-box; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; font-size: 13px; padding: 2px 4px; color: #c7254e; border-radius: 4px; ">true</code>.
     
    【解答】大致思路是,先二分查找找到在哪一行,再二分查找找到在该行上的哪一列。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    public class Solution {
        public boolean searchMatrix(int[][] matrix, int target) {
            if (null==matrix || matrix.length==0 || matrix[0].length==0)
                return false;
     
            int r = matrix.length-1;
            int c = matrix[0].length-1;
     
            // find the row
            int min = 0;
            int max = r;
            int row = -1;
            while (min< =max) {
                int mid = (max+min)/2;
                if (target==matrix[mid][0] || target==matrix[mid][c][/c]
     
     
    <pre class="java; toolbar: false;">)
                    return true;
     
                if (target<matrix[mid][0]) {
                    max = mid-1;
                    continue;
                } else if (target>matrix[mid]
     
     
     
    </pre>
    <pre class="java; toolbar: false;">) {
                    min = mid+1;
                    continue;
                } else {
                    row = mid;
                    break;
                }
            }
     
            if (row==-1)
                return false;
     
            // seek for the element
            min = 1;
            max = c-1;
            while (min<=max) {
                int mid = (min+max)/2;
                if (matrix[row][mid]==target)
                    return true;
                else if (matrix[row][mid]>target)
                    max = mid-1;
                else
                    min = mid+1;
            }
     
            return false;
        }
    }</pre>
    <p><strong><span id="search-for-a-range" style="color: #ff0000;">Search for a Range</span></strong></p>
    <p>【题目】<span style="color: #333333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 30px;">Given a sorted array of integers, find the starting and ending position of a given target value.</span></p>
    <p style="box-sizing: border-box; margin: 0px 0px 10px; color: #333333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 30px;">Your algorithm’s runtime complexity must be in the order of <em style="box-sizing: border-box;">O</em>(log <em style="box-sizing: border-box;">n</em>).</p>
    <p style="box-sizing: border-box; margin: 0px 0px 10px; color: #333333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 30px;">If the target is not found in the array, return <code style="box-sizing: border-box; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; font-size: 13px; padding: 2px 4px; color: #c7254e; border-radius: 4px; ">[-1, -1]</code>.</p>
    <p style="box-sizing: border-box; margin: 0px 0px 10px; color: #333333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 30px;">For example,<br style="box-sizing: border-box;" data-filtered="filtered">Given <code style="box-sizing: border-box; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; font-size: 13px; padding: 2px 4px; color: #c7254e; border-radius: 4px; ">[5, 7, 7, 8, 8, 10]</code> and target value 8,<br style="box-sizing: border-box;" data-filtered="filtered">return <code style="box-sizing: border-box; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; font-size: 13px; padding: 2px 4px; color: #c7254e; border-radius: 4px; ">[3, 4]</code>.</p>
    <p>【解答】核心思想还是二分查找,变化的地方在于,在每次循环中,如果找到了,这个时候需要对[min,mid-1]和[mid+1,max]这两段继续使用二分查找分别去寻找每段的分界线l和r,使得这两条分界线组成的range [l,r]为所求。</p>
    <pre class="java; toolbar: false;">public class Solution {
        public int[] searchRange(int[] A, int target) {
            if (null==A || A.length==0)
                return new int[]{-1,-1};
     
            int min = 0;
            int max = A.length-1;
     
            while (min<=max) {
                int mid = (max+min)/2;
                if (A[mid]<target) {
                    min = mid+1;
                    continue;
                } else if (A[mid]>target) {
                    max = mid-1;
                    continue;
                }
     
                // A[mid]==target, search for the range in [min,max]
                int l=mid;
                int r=mid;
     
                int lmax = mid-1;
                int rmin = mid+1;
     
                while (min<=lmax) {
                    int m = (min+lmax)/2;
                    if (A[m]<target) {
                        min = m+1;
                        continue;
                    } else {
                        lmax = m-1;
                        l = m;
                        continue;
                    }
                }
     
                while (max>=rmin) {
                    int m = (max+rmin)/2;
                    if (A[m]>target) {
                        max = m-1;
                        continue;
                    } else {
                        rmin = m+1;
                        r = m;
                        continue;
                    }
                }
     
                return new int[]{l,r};
            }
     
            return new int[]{-1,-1};
        }
    }</pre>
    <p><strong><span id="search-insert-position" style="color: #ff0000;">Search Insert Position</span></strong></p>
    <p>【题目】<span style="color: #333333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 30px;">Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.</span></p>
    <p style="box-sizing: border-box; margin: 0px 0px 10px; color: #333333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 30px;">You may assume no duplicates in the array.</p>
    <p style="box-sizing: border-box; margin: 0px 0px 10px; color: #333333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 30px;">Here are few examples.<br style="box-sizing: border-box;" data-filtered="filtered"><code style="box-sizing: border-box; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; font-size: 13px; padding: 2px 4px; color: #c7254e; border-radius: 4px; ">[1,3,5,6]</code>, 5 2<br style="box-sizing: border-box;" data-filtered="filtered"><code style="box-sizing: border-box; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; font-size: 13px; padding: 2px 4px; color: #c7254e; border-radius: 4px; ">[1,3,5,6]</code>, 2 1<br style="box-sizing: border-box;" data-filtered="filtered"><code style="box-sizing: border-box; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; font-size: 13px; padding: 2px 4px; color: #c7254e; border-radius: 4px; ">[1,3,5,6]</code>, 7 4<br style="box-sizing: border-box;" data-filtered="filtered"><code style="box-sizing: border-box; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; font-size: 13px; padding: 2px 4px; color: #c7254e; border-radius: 4px; ">[1,3,5,6]</code>, 0 0</p>
    <p>【解答】基于二分搜索,稍微改一下实现。如果找到了,皆大欢喜;如果没找到,要让指针最后处于left>right的状态,这样left就是要插入的位置。</p>
    <pre class="java; toolbar: false;">public class Solution {
        public int searchInsert(int[] nums, int target) {
            if (nums.length==0)
                return 0;
     
            int left=0, right=nums.length-1;
            while(left<=right) {
                int mid = (left+right)/2;
                if (nums[mid]==target)
                    return mid;
                else if (nums[mid]>target)
                    right = mid-1;
                else
                    left = mid+1;
            }
     
            // left>right
            return left;
        }
    }</pre>
    <p><strong><span id="search-in-rotated-sorted-array-ii" style="color: #ff0000;">Search in Rotated Sorted Array II</span></strong></p>
    <p>【题目】<span style="color: #333333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 30px;">Follow up for “Search in Rotated Sorted Array”:</span></p>
    <p style="box-sizing: border-box; margin: 0px 0px 10px; color: #333333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 30px;">What if <em style="box-sizing: border-box;">duplicates</em> are allowed?</p>
    <p style="box-sizing: border-box; margin: 0px 0px 10px; color: #333333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 30px;">Would this affect the run-time complexity? How and why?</p>
    <p style="box-sizing: border-box; margin: 0px 0px 10px; color: #333333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 30px;">Write a function to determine if a given target is in the array.</p>
    <p>【解答】相较于“Search In Rotated Sorted Array”(那道题是Hard难度的,这道题是Medium的),就多了下面16行的那个分支,在发现A[end]和A[start]相等的时候,我就不知道哪一侧才有最大最小值的分界点了。这个最糟糕情况下的复杂度就不再是log n了,而是n。</p>
    <pre class=" java; toolbar: false;">public class Solution {
        public boolean search(int[] A, int target) {
            return search(A, target, 0, A.length-1);
        }
     
        private boolean search(int[] A, int target, int start, int end) {
            if (start>end)
                return false;
     
            int mid = (end+start)/2;
     
            if (target==A[mid])
                return true;
     
            // in this case i don't know which part is increasing
            if (A[end]==A[mid]) {
                return search(A, target, start, mid-1) || search(A, target, mid+1, end-1);
            }
     
            if (A[end]>A[mid]) { // the right part is increasing
                if (target>A[end] || target<A[mid]) {
                    return search(A, target, start, mid-1);
                } else {
                    return search(A, target, mid+1, end);
                }
            } else { // the left part is increasing
                if (target<A[start] || target>A[mid]) {
                    return search(A, target, mid+1, end);
                } else {
                    return search(A, target, start, mid-1);
                }
            }
        }
    }</pre>
    <p><strong><font color="#ff0000">文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接<a href="http://www.raychase.net/2687">《四火的唠叨》</a></font></strong></p>
    <div class="open_social_box share_box"><div class="share_button share_icon_weibo" onclick="share_button_click('http://v.t.sina.com.cn/share/share.php?url=%URL%&title=%TITLE%&pic=%PIC%&appkey=&ralateUid=&language=zh_cn&searchPic=false',event)" title="分享到微博"></div><div class="share_button share_icon_qqzone" onclick="share_button_click('http://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=%URL%&title=%TITLE%&desc=&summary=&site=&pics=%PIC%',event)" title="分享到QQ空间"></div><div class="share_button share_icon_qqweibo" onclick="share_button_click('http://share.v.t.qq.com/index.php?c=share&a=index&url=%URL%&title=%TITLE%&pic=%PIC%&appkey=',event)" title="分享到QQ微博"></div><div class="share_button share_icon_youdao" onclick="share_button_click('http://note.youdao.com/memory/?url=%URL%&title=%TITLE%&sumary=&pic=%PIC%&product=',event)" title="分享到网易有道笔记"></div><div class="share_button share_icon_wechat" onclick="share_button_click('QRCODE',event)" title="分享到微信"></div><div class="share_button share_icon_twitter" onclick="share_button_click('http://twitter.com/home/?status=%TITLE%:%URL%',event)" title="分享到 Twitter"></div><div class="share_button share_icon_facebook" onclick="share_button_click('http://www.facebook.com/sharer.php?u=%URL%&t=%TITLE%',event)" title="分享到 Facebook"></div><div class="share_button share_icon_google" onclick="share_button_click('http://translate.google.com.hk/translate?hl=zh_CN&sl=zh-CN&tl=zh&u=%URL%',event)" title="谷歌翻译"></div><div class="open_social_qrcode" onclick="jQuery(this).hide();" data-filtered="filtered"></div></div><div class="yarpp-related">
    <p>你可能也喜欢看:</p><ol>
    <li><a href="http://www.raychase.net/2666" rel="bookmark" title="LeetCode题目解答——Medium部分(上)">LeetCode题目解答——Medium部分(上) </a></li>
    <li><a href="http://www.raychase.net/2685" rel="bookmark" title="LeetCode题目解答——Hard部分">LeetCode题目解答——Hard部分 </a></li>
    <li><a href="http://www.raychase.net/3044" rel="bookmark" title="LeetCode题目解答——155~226题">LeetCode题目解答——155~226题 </a></li>
    <li><a href="http://www.raychase.net/3534" rel="bookmark" title="LeetCode题目解答——第227到310题">LeetCode题目解答——第227310题 </a></li>
    <li><a href="http://www.raychase.net/2639" rel="bookmark" title="LeetCode题目解答——Easy部分">LeetCode题目解答——Easy部分 </a></li>
    </ol>
    </div>
        

    Posted in Algorithm & Data StructureTagged 

  • 相关阅读:
    设计模式:单一职责原则
    多线程的创建
    Android开发基础(java)14
    面向对象编程的思想(6)
    面向对象编程的思想(5)未完成
    面向对象编程的思想(4)
    面向对象编程的思想(3)
    面向对象编程的思想(2)
    面向对象编程的思想(1)
    GDB 命令详细解释
  • 原文地址:https://www.cnblogs.com/think90/p/8976398.html
Copyright © 2011-2022 走看看