Leetcode算法解析1-50

<center>#1 Two Sum</center>

  • link
  • Description:
    • Given an array of integers, return indices of the two numbers such that they add up to a specific target.
  • Input: [2, 7, 11, 15]
  • Output: [0, 1]
  • Assumptions:
    • each input would have exactly one solution
    • you may not use the same element twice
  • Solution:
    • Two Sum 的題一般有兩種解法, 一種是用hash, 一種是用兩根指針。對(duì)于本題來說,用hash只需要遍歷一遍數(shù)組,所以在時(shí)間復(fù)雜度上要優(yōu)于兩根指針,因?yàn)閮蓚€(gè)指針的方法是基于排序數(shù)組。
    • Java中提供了HashMap的實(shí)現(xiàn)。 Map以數(shù)組元素為Key, 以索引為Value。
    • 遍歷一遍數(shù)組, 先在map中尋找是否有元素能夠和當(dāng)前值組成sum為target, 這個(gè)查找是O(1)的復(fù)雜度, 如果沒有,就把當(dāng)前值和索引作為一個(gè)鍵值對(duì)保存在Map中供后續(xù)查找, 保存也是O(1)的復(fù)雜度。Worst Case是遍歷整個(gè)數(shù)組,也就是O(n)。
    • Code:
    # code block
    class Solution {
        public int[] twoSum(int[] nums, int target) {
            /*
             * Two Sum的題變種很多,所以第一個(gè)要關(guān)注的就是Assumption,本題是
             * 最原始的two sum, 只有一個(gè)答案, 并且同一個(gè)元素不能用兩次
             *
             */
            // 校驗(yàn):本題的assumption確保有至少一個(gè)解,所以該步可以省略
            if (nums == null || nums.length < 2) {
                return new int[0];
            }
            // intialize the result
            int[] result = new int[0];
            Map<Integer, Integer> map = new HashMap<>();
    
            for (int i = 0; i < nums.length; i++) {
                if (map.containsKey(target - nums[i])) {
                    result = new int[2];
                    result[0] = map.get(target - nums[i]);
                    result[1] = i;
                    return result;
                }
                map.put(nums[i], i);
            }
    
            return result;
        }
    }
    
  • Time Complexity: O(n)
  • Space Complexity: O(n)

<center>#2 Add Two Numbers</center>

  • link
  • Description:
    • You are given two non-empty linked lists representing two non-negative integers. 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.
    • You may assume the two numbers do not contain any leading zero, except the number 0 itself.
  • Input:
  • Output:
  • Assumptions:
    • two numbers do not contain any leading zero, except the number 0 itself.
  • Solution:
    • 注意點(diǎn):
      • 使用dummy node
      • 處理到所有case:
        • l1 或 l2 位 null
        • l1 l2長度相等
        • l1 長于 l2
        • l2 長于 l1
        • 答案需要進(jìn)位
  • Code:
    # code block
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        if (l1 == null) {
            return l2;
        }
        if (l2 == null) {
            return l1;
        }
        int carrier = 0;
        ListNode dummy = new ListNode(0);
        ListNode curr = dummy;
        while (l1 != null && l2 != null) {
            int val = l1.val + l2.val + carrier;
            curr.next = new ListNode(val % 10);
            curr = curr.next;
            carrier = val / 10;
            l1 = l1.next;
            l2 = l2.next;
        }
        while (l1 != null) {
            int val = l1.val + carrier;
            curr.next = new ListNode(val % 10);
            curr = curr.next;
            carrier = val / 10;
            l1 = l1.next;
        }
        while (l2 != null) {
            int val = l2.val + carrier;
            curr.next = new ListNode(val % 10);
            curr = curr.next;
            carrier = val / 10;
            l2 = l2.next;
        }
        if (carrier != 0) {
            curr.next = new ListNode(carrier);
        }
        return dummy.next;
    }
    
  • Time Complexity: O(max(m, n))
  • Space Complexity: O(1)

<center>#3 Longest Substring Without Repeating Characters</center>

  • link
  • Description:
    • Given a string, find the length of the longest substring without repeating characters.
  • Input: "abcabcbb"
  • Output: 3
  • Solution:
    • 典型的同向型兩根指針問題的區(qū)間類問題。
    • 維持一個(gè)符合條件的區(qū)間,每次更新最大值
  • Code:
    # code block
    public int lengthOfLongestSubstring(String s) {
        if (s == null || s.length() == 0) {
            return 0;
        }
        boolean[] hash = new boolean[256];
        char[] sc = s.toCharArray();
        int result = 0;
        int left = 0, right = 0;
        while (right < sc.length) {
            while (right < sc.length && hash[sc[right]] == false) {
                hash[sc[right]] = true;
                right++;
            }
            result = Math.max(result, right - left);
            hash[sc[left]] = false;
            left++;
        }
        return result;
    }
    
  • Time Complexity: O(n)
  • Space Complexity: O(1)

<center>#4 Median of Two Sorted Arrays</center>

  • link
  • Description:
    • There are two sorted arrays nums1 and nums2 of size m and n respectively.
    • Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
  • Input: nums1 = [1, 2] nums2 = [3, 4]
  • Output:2.5
  • Assumptions:
    • At least one of this two arrays has more than 0 element.
  • Solution:
    • 要求時(shí)間復(fù)雜度為O(log (m+n)), 這題可以由時(shí)間復(fù)雜度倒推方法
    • 滿足O(log n)時(shí)間復(fù)雜度的算法第一個(gè)想到的是二分,又是排序數(shù)組,更加確定是二分
    • 求第k個(gè)數(shù),每次把問題縮小到排除不可能的k / 2 個(gè)數(shù)
    • 對(duì)兩個(gè)數(shù)組同時(shí)找到第k / 2 個(gè)數(shù),如果第一個(gè)數(shù)組中的數(shù)小于第二個(gè)數(shù)組中的數(shù),那么第一個(gè)數(shù)組中必然不存在第k大的數(shù),可以排除前k / 2 的數(shù),反之同理
    • 遞歸執(zhí)行,注意遞歸的出口和corner case
  • Code:
    # code block
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        if (nums1 == null) {
            // prevent null exception
            nums1 = new int[0];
        }
        if (nums2 == null) {
            nums2 = new int[0];
        }
        int m = nums1.length;
        int n = nums2.length;
        // 1-based index
        int k = (m + n + 1) / 2;
        int median1 = findK(nums1, nums2, 0, 0, k);
        if ((m + n) % 2 == 1) {
            // total number is odd
            return (double)median1;
        }
        int median2 = findK(nums1, nums2, 0, 0, k + 1);
        // Remind of the conversion from int to double
        return (median1 + median2) / 2.0;
    }
    
    private int findK(int[] nums1, int[] nums2, int start1, int start2, int k) {
        int m = nums1.length, n = nums2.length;
        if (start1 >= m) {
            return nums2[start2 + k - 1];
        }
        if (start2 >= n) {
            return nums1[start1 + k - 1];
        }
        if (k == 1) {
            return Math.min(nums1[start1], nums2[start2]);
        }
        int mid = k / 2;
         // make sure never drop the val1 when start1 + mid - 1 >= m
        int val1 = (start1 + mid - 1 < m) ? nums1[start1 + mid - 1] : Integer.MAX_VALUE;
        int val2 = (start2 + mid - 1 < n) ? nums2[start2 + mid - 1] : Integer.MAX_VALUE;
        if (val1 < val2) {
            // drop amount of mid number in nums1
            return findK(nums1, nums2, start1 + mid, start2, k - mid);
        } else {
            return findK(nums1, nums2, start1, start2 + mid, k - mid);
        }
    }
    
  • Time Complexity: O(log (m+n))
  • Space Complexity: O(1)

<center>#5 Longest Palindromic Substring</center>

  • link
  • Description:
    • Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
  • Input: "babad"
  • Output: "bab" or "aba"
  • Solution:
    • 求最長子回文串,brute force的解法是三重循環(huán),一重確定起點(diǎn),一重確定終點(diǎn),一重驗(yàn)證是否為回文串。復(fù)雜度為O(n^3)
    • 這邊想到的優(yōu)化是如果我以一個(gè)點(diǎn)為中點(diǎn),來找以這個(gè)點(diǎn)為中點(diǎn)的最長回文串
    • 這樣一重循環(huán)確定中點(diǎn),一重循環(huán)找最長回文串,時(shí)間復(fù)雜度就降到O(n^2)
    • 注意點(diǎn)是回文串要考慮到奇數(shù)長度和偶數(shù)長度兩種特殊情形
  • Code:
    # code block
    public String longestPalindrome(String s) {
        if (s == null || s.length() < 2) {
            return s;
        }
        for (int i = 0; i < s.length(); i++) {
            updateLongest(s, i, i);
            if (i != s.length() - 1 && s.charAt(i) == s.charAt(i + 1)) {
                updateLongest(s, i, i + 1);
            }
        }
        return s.substring(start, end + 1);
    }
    
    private void updateLongest(String s, int l, int r) {
        while (l >= 0 && r < s.length()) {
            if (s.charAt(l) != s.charAt(r)) {
                break;
            }
            if (r - l > end - start) {
                start = l;
                end = r;
            }
            l--;
            r++;
        }
    }
    
    private int start = 0;
    private int end = 0;
    
  • Time Complexity: O(n ^ 2)
  • Space Complexity: O(1)

<center>#6 ZigZag Conversion</center>

  • link
  • Description:
    • 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)
  • Input: "PAYPALISHIRING", 3
  • Output: "PAHNAPLSIIGYIR"
  • Solution:
    • 借用網(wǎng)上摘來的圖表達(dá)題意
    • [圖片上傳失敗...(image-eb60d7-1534568626309)]
    • 這是一道模擬題,可以找到規(guī)律是以2 * numRows - 2 在循環(huán)
    • corner case: numRos = 1 時(shí)就是原字符串
  • Code:
    # code block
    public String convert(String s, int numRows) {
        if (s == null || s.length() <= numRows || numRows <= 1) {
            return s;
        }
        int sep = numRows * 2 - 2;
        char[] sc = s.toCharArray();
        StringBuilder[] sb = new StringBuilder[numRows];
        for (int i = 0; i < sb.length; i++) {
            sb[i] = new StringBuilder();
        }
        for (int i = 0; i < sc.length; i++) {
            int idx = i % sep;
            if (idx < numRows) {
                sb[idx].append(sc[i]);
            } else {
                sb[sep - idx].append(sc[i]);
            }
        }
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < sb.length; i++) {
            result.append(sb[i]);
        }
        return result.toString();
    }
    
  • Time Complexity: O(n)
  • Space Complexity: O(n)

<center>#7 Reverse Integer</center>

  • link
  • Description: Reverse Integer
    • Reverse digits of an integer.
  • Input: 123
  • Output: 321
  • Assumptions:
    • return 0 when the reversed integer overflows.
  • Solution:
    • 注意點(diǎn):
      • 檢查overflow,注釋位置為詳細(xì)解法
      • 注意java負(fù)數(shù)取余為負(fù)數(shù)
  • Code:
    # code block
    public int reverse(int x) {
        boolean isPos = (x >= 0) ? true : false;
        int result = 0;
        while (x / 10 != 0 || x % 10 != 0) {
            int val = result * 10 + Math.abs(x % 10);
            // check overflow
            if (val / 10 != result) {
                return 0;
            }
            result = val;
            x /= 10;
        }
        return isPos? result : -result;
    }
    
  • Time Complexity: O(number of digits)
  • Space Complexity: O()

<center>#8 String to Integer (atoi)</center>

  • link
  • Description:
    • 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.
  • Input: " -0012a42"
  • Output: -12
  • Solution:
    • 注意考慮所有Corner Case:
      • null
      • empty string
      • positive or negative
      • start with space
      • overflow (max and min are different)
    • 方法二:轉(zhuǎn)換成long型來判斷
  • Code:
    # code block
    public int myAtoi(String str) {
        if (str == null) {
            // prevent null exception
            return 0;
        }
        str = str.trim();
        if (str.length() == 0) {
            // emtpy string
            return 0;
        }
        int result = 0;
        boolean isPos = true;
        char[] sc = str.toCharArray();
        int i = 0;
        // postive or negative
        if (Character.isDigit(sc[0])) {
            isPos = true;
        } else if (sc[0] == '+') {
            isPos = true;
            i = 1;
        } else if (sc[0] == '-') {
            isPos = false;
            i = 1;
        } else {
            return 0;
        }
        for (; i < sc.length; i++) {
            if (!Character.isDigit(sc[i])) {
                // invalid string
                return result;
            }
            if (isPos) {
                int val = result * 10 + sc[i] - '0';
                if (val / 10 != result) {
                    // check overflow
                    return Integer.MAX_VALUE;
                }
                result = val;
            } else {
                int val = result * 10 + '0' - sc[i];
                if (val / 10 != result) {
                    return Integer.MIN_VALUE;
                }
                result = val;
            }
        }
        return result;
    }
    
  • Time Complexity: O(n)
  • Space Complexity: O(n)

<center>#9 Palindrome Number</center>

  • link
  • Description:
    • Determine whether an integer is a palindrome. Do this without extra space.
  • Input: 121
  • Output: true
  • Solution:
    • 注意點(diǎn):
      • 負(fù)數(shù)肯定不是
      • 注意溢出
  • Code:
    # code block
    public boolean isPalindrome(int x) {
        if (x < 0) {
            return false;
        }
        long org = x;
        long reverse = 0;
        while (x / 10 != 0 || x % 10 != 0) {
            reverse = reverse * 10 + x % 10;
            x /= 10;
        }
        return reverse == org;
    }
    
  • Time Complexity: O(lg n)
  • Space Complexity: O(1)

<center>#11 Container With Most Water</center>

  • link
  • Description:
    • Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.
  • Input: [1,2,1]
  • Output: 2
  • Assumptions:
    • You may not slant the container and n is at least 2.
  • Solution:
    • 典型的相向型兩根指針問題。面積等于底乘高。當(dāng)兩根指針相向, 底只會(huì)越來越小,所以只需要找到更高的高與當(dāng)前最大值比較即可。
    • 兩根線和x軸組成容器,選取短板做高,所以每次更新短板,就能更新高度。
  • Code:
    # code block
    public int maxArea(int[] height) {
        if (height == null || height.length <= 1) {
            return 0;
        }
        int max = 0;
        int left = 0, right = height.length - 1;
        while (left < right) {
            max = Math.max(max, Math.min(height[left], height[right]) * (right - left));
            if (height[left] < height[right]) {
                left++;
            } else {
                right--;
            }
        }
        return max;
    }
    
  • Time Complexity: O(n)
  • Space Complexity: O(1)

<center>#12 Integer to Roman</center>

  • link
  • Description:
    • Given an integer, convert it to a roman numeral.
  • Input: 456
  • Output: "CDLVI"
  • Assumptions:
    • Input is guaranteed to be within the range from 1 to 3999.
  • Solution:
    • 模擬題,要想知道羅馬數(shù)字的具體規(guī)則參考wikipedia
  • Code:
    # code block
    class Solution {
        public static final int[] number = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        public static final String[] symbol = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
        public String intToRoman(int num) {
            StringBuilder sb = new StringBuilder();
            int i = 0;
            while (num != 0) {
                int times = num / number[i];
                if (times != 0) {
                    for (int j = 0; j < times; j++) {
                        sb.append(symbol[i]);
                    }
                    num = num % number[i];
                }
                i++;
            }
            return sb.toString();
        }
    }
    

<center>#13 Roman to Integer</center>

  • link
  • Description:
    • Given a roman numeral, convert it to an integer.
  • Input: "CDLVI"
  • Output: 456
  • Assumptions:
    • Input is guaranteed to be within the range from 1 to 3999.
  • Solution:
    • 模擬題,要想知道羅馬數(shù)字的具體規(guī)則參考wikipedia
  • Code:
    # code block
    class Solution {
        public static final int[] number = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        public static final String[] symbol = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
        public int romanToInt(String s) {
            int result = 0;
            if (s == null || s.length() == 0) {
                return result;
            }
            StringBuilder sb = new StringBuilder(s);
            int i = 0;
            while (sb.length() > 0 && i < number.length) {
                String curr = symbol[i];
                while (sb.length() >= curr.length() && sb.substring(0, curr.length()).equals(curr)) {
                    result += number[i];
                    sb.delete(0, curr.length());
                }
                i++;
            }
            return result;
        }
    }
    

<center>#14 Longest Common Prefix</center>

  • link
  • Description:
    • Write a function to find the longest common prefix string amongst an array of strings.
  • Input:["aasdfgas", "aaasafda"]
  • Output:"aa"
  • Solution:
    • 多種解法
    • 最巧妙地是排序之后比較首尾
    • 二分也可以通過測試
  • Code:
    # code block
    public String longestCommonPrefix(String[] strs) {
        StringBuilder result = new StringBuilder();
    
        if (strs!= null && strs.length > 0){
    
            Arrays.sort(strs);
    
            char [] a = strs[0].toCharArray();
            char [] b = strs[strs.length-1].toCharArray();
    
            for (int i = 0; i < a.length; i ++){
                if (b.length > i && b[i] == a[i]){
                    result.append(b[i]);
                }
                else {
                    return result.toString();
                }
            }
        return result.toString();
    }
    
  • Time Complexity: O(n lg n)
    • Code:
    # code block
    class Solution {
        public String longestCommonPrefix(String[] strs) {
            if (strs == null || strs.length == 0) {
                return "";
            }
            int start = 0;
            int end = Integer.MAX_VALUE;
            for (String str : strs) {
                end = Math.min(end, str.length());
            }
            end--;
            while (start < end - 1) {
                int mid = start + (end - start) / 2;
                if (checkValid(mid, strs)) {
                    start = mid;
                } else {
                    end = mid;
                }
            }
    
            if (checkValid(end, strs)) {
                return strs[0].substring(0, end + 1);
            }
            if (checkValid(start, strs)) {
                return strs[0].substring(0, start + 1);
            }
            return "";
        }
    
        private boolean checkValid(int n, String[] strs) {
            String tmp = strs[0].substring(0, n + 1);
            for(int i = 1; i < strs.length; i++) {
                if (!strs[i].substring(0, n + 1).equals(tmp)) {
                    return false;
                }
            }
            return true;
        }
    }
    

<center>#15 3Sum</center>

  • link
  • Description:
    • Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
  • Input: [-1, 0, 1, 2, -1, -4]
  • Output: [[-1, 0, 1], [-1, -1, 2]]
  • Assumptions:
    • The solution set must not contain duplicate triplets
  • Solution:
    • Two sum 變種??梢院喕癁楣潭ㄒ粋€(gè)值,在剩下的值中尋找two sum。難點(diǎn)在于去重,去重即聯(lián)想到排序!所以先把數(shù)組排序。if (i != 0 && nums[i] == nums[i - 1]) 就continue這種方法確保了每個(gè)數(shù)字只取第一個(gè)。在two sum中兩個(gè)while循環(huán)確保相同的組合只出現(xiàn)一次。
  • Code:
    # code block
    public List<List<Integer>> threeSum(int[] nums) {
         /*
          * 2 Sum的變種, 可以簡化為確定一個(gè)數(shù)a, 在剩下的數(shù)中的2 Sum為target - a
          * 難點(diǎn),去重
          */
         // Initialize the result
         List<List<Integer>> result = new ArrayList<>();
         // Boundary case
         if (nums == null || nums.length < 3) {
             return result;
         }
         // The two pointer solution for two sum is based on sorted array
         Arrays.sort(nums);
         for (int i = 0; i < nums.length - 2; i++) {
             // remove duplicates
             if (i != 0 && nums[i] == nums[i - 1]) {
                 continue;
             }
             int left = i + 1, right = nums.length - 1;
             while (left < right) {
                 int val = nums[i] + nums[left] + nums[right];
                 if (val == 0) {
                     // find one solution
                     List<Integer> solution = new ArrayList<>();
                     solution.add(nums[i]);
                     solution.add(nums[left]);
                     solution.add(nums[right]);
                     result.add(solution);
                     left++;
                     right--;
                     // remove duplicates
                     while (left < right && nums[left] == nums[left - 1]) {
                         left++;
                     }
                     while (left < right && nums[right] == nums[right + 1]) {
                         right--;
                     }
                 } else if (val > 0) {
                     // drop the right
                     right--;
                 } else {
                     // drop the left
                     left++;
                 }
             }
         }
         return result;
     }
    
    
  • Time Complexity: O(n ^ 2)
  • Space Complexity: O(1)

<center>#16 3Sum Closest</center>

  • link

  • Description:

    • 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.
  • Input: [2, 7, 11, 15]

  • Output: [0, 1]

  • Assumptions:

    • assume that each input would have exactly one solution.
  • Solution:

    • 3Sum的變種, 保存一個(gè)與target的差值, 每次作比較,小于這個(gè)差值即更新答案。
      • 注意點(diǎn):
      • 三個(gè)int相加使用long防止溢出
  • Code:

    # code block
    public int threeSumClosest(int[] nums, int target) {
         Arrays.sort(nums);
         long diff = (long)Integer.MAX_VALUE * 2;
         int result = 0;
        // 3 Sum template
         for (int i = 0; i < nums.length - 2; i++) {
             int left = i + 1, right = nums.length - 1;
             while (left < right) {
                 long val = nums[i] + nums[left] + nums[right];
                 if (Math.abs(val - target) < Math.abs(diff)) {
                     diff = val - target;
                     result = (int)val;
                 }
           // when val == target, it must be the answer
                 if (val == target) {
                     return target;
                 } else if (val > target) {
                     right--;
                 } else {
                     left++;
                 }
             }
         }
         return result;
     }
    
    
  • Time Complexity: O(n ^ 2)

  • Space Complexity: O(n)

<center>#17 Letter Combinations of a Phone Number</center>

  • link
  • Description:
    • 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.
    • avator
  • Input: Digit string "23"
  • Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]
  • Assumptions:
    • The digits are between 2 and 9
  • Solution:
    • 求組合的題,在隱式圖上做DFS
    • 注意點(diǎn):
      • 沒有明顯的回溯痕跡是因?yàn)镾tring是immutable的類,每次修改都會(huì)形成一個(gè)新的對(duì)象
  • Code:
    # code block
    class Solution {
        public List<String> letterCombinations(String digits) {
            List<String> result = new ArrayList<>();
            if (digits == null || digits.length() == 0) {
                return result;
            }
            char[] dc = digits.toCharArray();
            dfsHelper(dc, 0, "", result);
            return result;
        }
    
        private void dfsHelper(char[] dc, int start, String curr, List<String> result) {
            if (start == dc.length) {
                result.add(curr);
                return;
            }
            char[] letters = pad[dc[start] - '0'].toCharArray();
            for (char letter : letters) {
                dfsHelper(dc, start + 1, curr + letter, result);
            }
        }
    
        public static final String[] pad = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
    }
    

<center>#18 4Sum</center>

  • link
  • Description:
    • Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.
  • Input: [1, 0, -1, 0, -2, 2]
  • Output: [[-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2]]
  • Assumptions:
    • solution set must not contain duplicate quadruplets.
  • Solution:
    • Two sum 變種。固定兩個(gè)值,在剩下的值中做two sum,注意點(diǎn)和3Sum一樣還是排序和去重。
  • Code:
    # code block
    public List<List<Integer>> fourSum(int[] nums, int target) {
          List<List<Integer>> result = new ArrayList<>();
          if (nums == null || nums.length < 4) {
              return result;
          }
          Arrays.sort(nums);
          for (int i = 0; i < nums.length - 3; i++) {
              if (i != 0 && nums[i] == nums[i - 1]) {
                  continue;
              }
              for (int j = i + 1; j < nums.length - 2; j++) {
                  if (j != i + 1 && nums[j] == nums[j - 1]) {
                      continue;
                  }
                  twoSum(nums, j + 1, target, nums[i], nums[j], result);
              }
          }
          return result;
      }
    
      private void twoSum(int[] nums, int left, int target, int v1, int v2, List<List<Integer>> result) {
          int right = nums.length - 1;
          while (left < right) {
              long val = v1 + v2 + nums[left] + nums[right];
              if (val == target) {
                  List<Integer> solution = new ArrayList<>();
                  solution.add(v1);
                  solution.add(v2);
                  solution.add(nums[left]);
                  solution.add(nums[right]);
                  result.add(solution);
                  left++;
                  right--;
                  while (left < right && nums[left] == nums[left - 1]) {
                      left++;
                  }
                  while (left < right && nums[right] == nums[right + 1]) {
                      right--;
                  }
              } else if (val > target) {
                  right--;
              } else {
                  left++;
              }
          }
      }
    
    
  • Time Complexity: O(n ^ 3)
  • Space Complexity: O(1)

<center>#19 Remove Nth Node From End of List</center>

  • link
  • Description:
    • Given a linked list, remove the nth node from the end of list and return its head.
  • Input: 1->2->3->4->5 n = 2
  • Output: 1->2->3->5
  • Assumptions:
    • Given n will always be valid.
    • Try to do this in one pass.
  • Solution:
    • 使用dummynode因?yàn)榭赡芤獎(jiǎng)h除第一個(gè)元素
    • 使用兩根指針保持距離即可
  • Code:
    # code block
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0);
        ListNode slow = dummy;
        ListNode fast = dummy;
        dummy.next = head;
        for (int i = 0; i < n; i++) {
            if (fast == null) {
                return head;
            }
            fast = fast.next;
        }
        while (fast.next != null) {
            fast = fast.next;
            slow = slow.next;
        }
        slow.next = slow.next.next;
        return dummy.next;
    }
    
  • Time Complexity: O(n)
  • Space Complexity: O(1)

<center>#20 Valid Parentheses</center>

  • link
  • Description:
    • 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.
  • Input: "()[]{}"
  • Output: true
  • Solution:
    • 模擬題,用棧來做最適合,注意corner case
  • Code:
    # code block
    public boolean isValid(String s) {
        if (s == null || s.length() == 0) {
            return true;
        }
        char[] sc = s.toCharArray();
        Stack<Character> stack = new Stack<>();
        for (char c : sc) {
            if (c == '(' || c == '[' || c == '{') {
                stack.push(c);
            } else {
                if (stack.isEmpty()) {
                    return false;
                }
                if (c == ')' && stack.peek() != '(') {
                    return false;
                }
                if (c == ']' && stack.peek() != '[') {
                    return false;
                }
                if (c == '}' && stack.peek() != '{') {
                    return false;
                }
                stack.pop();
            }
        }
        return stack.isEmpty();
    }
    
  • Time Complexity: O(n)
  • Space Complexity: O(n)

<center>#21 Merge Two Sorted Lists</center>

  • link
  • Description:
    • 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.
  • Input: 1->3->5->null 2->4->6->7->null
  • Output:1->2->3->4->5->6->7->null
  • Solution:
    • 歸并排序
    • 注意所有corner case
  • Code:
    # code block
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) {
            return l2;
        }
        if (l2 == null) {
            return l1;
        }
        ListNode dummy = new ListNode(0);
        ListNode curr = dummy;
        while (l1 != null && l2 != null) {
            ListNode tmp = null;
            if (l1.val < l2.val) {
                tmp = l1;
                l1 = l1.next;
                tmp.next = null;
            } else {
                tmp = l2;
                l2 = l2.next;
                tmp.next = null;
            }
            curr.next = tmp;
            curr = curr.next;
        }
        if (l1 != null) {
            curr.next = l1;
        }
        if (l2 != null) {
            curr.next = l2;
        }
        return dummy.next;
    }
    
  • Time Complexity: O(m + n)
  • Space Complexity: O(1)

<center>#22 Generate Parentheses</center>

  • link
  • Description:
    • Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
  • Input: 3
  • Output: ["((()))", "(()())", "(())()", "()(())", "()()()"]
  • Solution:
    • 排列題,用隱式圖DFS遍歷
    • String 是immutable類,不用特意回溯
    • 剪枝: 當(dāng)左邊括號(hào)和右邊括號(hào)數(shù)量相等時(shí),可以省去加右邊括號(hào)的搜索
  • Code:
    # code block
    public List<String> generateParenthesis(int n) {
        List<String> result = new ArrayList<>();
        if (n < 0) {
            return result;
        }
        dfsHelper(0, 0, n, "", result);
        return result;
    }
    
    private void dfsHelper(int left, int right, int n, String state, List<String> result) {
        if (left == n && right == n) {
            result.add(state);
        }
        if (left < n) {
            dfsHelper(left + 1, right, n, state + "(", result);
        }
        // 剪枝
        if (right < left) {
            dfsHelper(left, right + 1, n, state + ")", result);
        }
    }
    

<center>#23 Merge k Sorted Lists</center>

  • link
  • Description:
    • Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
  • Input: [[1,2],[3,6],[4,5]];
  • Output:[1,2,3,4,5,6]
  • Solution:
    • 歸并k個(gè)list,往歸并排序的方向去想
    • 歸并排序兩個(gè)list中去小的那一個(gè)的值,k個(gè)list就是取k個(gè)list中最小的那個(gè)值
    • 聯(lián)想到heap, java中有heap的不完全實(shí)現(xiàn)priorityqueue
    • 要學(xué)會(huì)寫java中的comparator
  • Code:
    # code block
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists == null || lists.length == 0) {
            return null;
        }
        ListNode dummy = new ListNode(0);
        ListNode curr = dummy;
        Queue<ListNode> pq = new PriorityQueue<>(lists.length + 1, new Comparator<ListNode>() {
            @Override
            public int compare(ListNode a, ListNode b) {
                return a.val - b.val;
            }
        });
        for (ListNode node : lists) {
            if (node != null) {
                pq.offer(node);
            }
        }
        while (!pq.isEmpty()) {
            ListNode min = pq.poll();
            if (min.next != null) {
                pq.offer(min.next);
            }
            curr.next = min;
            curr = curr.next;
        }
        return dummy.next;
    }
    
  • Time Complexity: O(n lg k)
  • Space Complexity: O(1)

<center>#24 Swap Nodes in Pairs</center>

  • link
  • Description:
    • Given a linked list, swap every two adjacent nodes and return its head.
  • Input: 1->2->3->4
  • Output: 2->1->4->3
  • Assumptions:
    • Your algorithm should use only constant space.
    • You may not modify the values in the list, only nodes itself can be changed.
  • Solution:
    • 鏈表reverse題,注意哪些指針要改變,注意子函數(shù)該返回什么,注意空鏈表處理
  • Code:
    # code block
    public ListNode swapPairs(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode prev = dummy;
        while ((prev = swap(prev)) != null);
        return dummy.next;
    }
    
    // prev->n1->n2->next
    // prev->n2->n1->next;
    // return n1 if swap, else return null
    private ListNode swap(ListNode prev) {
        if (prev.next == null || prev.next.next == null) {
            // No swap needed
            return null;
        }
        ListNode n1 = prev.next;
        ListNode n2 = n1.next;
        ListNode next = n2.next;
        prev.next = n2;
        n2.next = n1;
        n1.next = next;
        return n1;
    }
    
  • Time Complexity: O(n)
  • Space Complexity: O(1)

<center>#25 Reverse Nodes in k-Group</center>

  • link
  • Description:
    • Given a linked list, reverse the nodes of a linked list k at a time and return its modified list.
    • k is a positive integer and is less than or equal to the length of the linked list. If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is.
    • You may not alter the values in the nodes, only nodes itself may be changed.
    • Only constant memory is allowed.
  • Input: 1->2->3->4->5 k = 2
  • Output: 2->1->4->3->5
  • Solution:
    • 翻轉(zhuǎn)時(shí)注意判斷是否需要翻轉(zhuǎn),哪些指針需要被改變,返回什么節(jié)點(diǎn)
  • Code:
    # code block
    public ListNode reverseKGroup(ListNode head, int k) {
        if (k == 1) {
            return head;
        }
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode prev = dummy;
        while ((prev = reverseK(prev, k)) != null);
        return dummy.next;
    }
    
    // prev->n1->n2->n3...->nk->next
    // prev->nk->nk-1->...->n1->next;
    // return n1
    private ListNode reverseK(ListNode prev, int k) {
        ListNode nk = prev;
        for (int i = 0; i < k; i++) {
            nk = nk.next;
            if (nk == null) {
                return null;
            }
        }
        ListNode n1 = prev.next;
        ListNode nkPlus = nk.next;
        ListNode left = prev;
        ListNode right = prev.next;
        while (right != nkPlus) {
            ListNode tmp = right.next;
            right.next = left;
            left = right;
            right = tmp;
        }
        prev.next = nk;
        n1.next = nkPlus;
        return n1;
    }
    
  • Time Complexity: O(n)
  • Space Complexity: O(1)

<center>#26 Remove Duplicate</center>

  • link
  • Description:
    • 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.
  • Input: [1,1,2]
  • Output: 2
  • Assumptions:
    • Do not allocate extra space for another array, you must do this in place with constant memory.
  • Solution:
    • 考的就是最基本的去重, 關(guān)鍵在于注釋了remove the duplicate的那個(gè)if判斷
  • Code:
    # code block
    public int removeDuplicates(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int idx = 0;
        for (int i = 0; i < nums.length; i++) {
    // remove the duplicate
            if (i != 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            nums[idx] = nums[i];
            idx++;
        }
        return idx;
    }
    
    
  • Time Complexity: O(n)
  • Space Complexity: O(1)

<center>#27 Remove Element</center>

  • link
  • Description:
    • Given an array and a value, remove all instances of that value in place and return the new length.
    • Do not allocate extra space for another array, you must do this in place with constant memory.
    • The order of elements can be changed. It doesn't matter what you leave beyond the new length.
  • Input: [3,2,2,3]
  • Output: 2
  • Assumptions:
    • Do not allocate extra space for another array, you must do this in place with constant memory.
  • Solution:
    • 考的就是最基本的數(shù)組in-place去除元素, 關(guān)鍵在于注釋了remove the duplicate的那個(gè)if判斷
  • Code:
    # code block
    public int removeElement(int[] nums, int val) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int idx = 0;
        for (int i = 0; i < nums.length; i++) {
    // remove duplicates
            if (nums[i] == val) {
                continue;
            }
            nums[idx++] = nums[i];
        }
        return idx;
    }
    
    
  • Time Complexity: O(n)
  • Space Complexity: O(1)

<center>#28 Implement strStr()</center>

  • link
  • Description:
    • Implement strStr().
    • Returns the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.
  • Input: "asdf" "sd"
  • Output: 1
  • Assumptions:
    • 有個(gè)更優(yōu)的算法叫KMP算法,此處不做介紹,有興趣研究可以谷歌或百度
  • Solution:
    • 簡單的兩重循環(huán),注意corner case
  • Code:
    # code block
    public int strStr(String haystack, String needle) {
        if (haystack == null || needle == null) {
            return -1;
        }
        if (needle.length() == 0) {
            return 0;
        }
        if (haystack.length() == 0) {
            return -1;
        }
        for (int i = 0; i < haystack.length() - needle.length() + 1; i++) {
            boolean flag = true;
            for (int j = 0; j < needle.length() && j + i < haystack.length(); j++) {
                if (needle.charAt(j) != haystack.charAt(j + i)) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                return i;
            }
        }
        return -1;
    }
    
  • Time Complexity: O(n ^ 2)
  • Space Complexity: O(1)

<center>#29 Divide Two Integers</center>

  • link
  • Description:
    • Divide two integers without using multiplication, division and mod operator.
    • If it is overflow, return MAX_INT.
  • Input: 123121 67
  • Output: 1837
  • Solution:
    • 求商不能使用除,余還有乘操作,那么思路一般就是使用位運(yùn)算
    • brute force的方法是累加divisor絕對(duì)值直到大于dividend
    • 但是使用位運(yùn)算,左移一位就是兩倍,這題可以用對(duì)divisor移位來簡化復(fù)雜度,從n的數(shù)量級(jí)到lg n
    • 這題的難點(diǎn)在于對(duì)每一個(gè)細(xì)節(jié)的處理,正負(fù),溢出等等,包括考到正負(fù)數(shù)最大值的絕對(duì)值差1
  • Code:
    # code block
    public int divide(int dividend, int divisor) {
        if (divisor == 0) {
            return Integer.MAX_VALUE;
        }
        boolean isPos = true;
        if ((dividend < 0 && divisor > 0) || (dividend > 0 && divisor < 0)) {
            isPos = false;
        }
        long dividendL = Math.abs((long)dividend);
        long divisorL = Math.abs((long)divisor);
        if (dividendL < divisorL) {
            return 0;
        }
        long factor = 1;
        long cmp = divisorL;
        long result = 0;
        while ((cmp << 1) <= dividendL) {
            cmp <<= 1;
            factor <<= 1;
        }
        while (factor > 0 && dividendL > 0) {
            if (dividendL >= cmp) {
                result += factor;
                dividendL -= cmp;
            }
            factor >>= 1;
            cmp >>= 1;
        }
        if (isPos) {
            return (result < Integer.MAX_VALUE) ? (int)result : Integer.MAX_VALUE;
        } else {
            return (-result > Integer.MIN_VALUE) ? (int)-result : Integer.MIN_VALUE;
        }
    }
    
  • Time Complexity: O(lg n)
  • Space Complexity: O(1)

<center>#31 Next Permutation</center>

  • link
  • Description:
    • 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.
  • Input: 1,2,3
  • Output: 1,3,2
  • Solution:
    • permutation中比較難的題, 這題先將排列的樹畫出來,找規(guī)律
    • A_NEXT一定與A有盡可能長的公共前綴。
    • 如果元素從底向上一直處于遞增,說明是對(duì)這些元素來說,是沒有下一個(gè)排列的
    • 從后向前比較相鄰的兩個(gè)元素,直到前一個(gè)元素小于后一個(gè)元素,停止,這樣確保A_NEXT 與A有最長的公共前綴
    • 下一步是找交換的點(diǎn),必然是找從底部開始第一個(gè)大于停止的那個(gè)元素進(jìn)行交換,能確保最小
    • 如果從低到根都是遞增,那么下一個(gè)元素就是reverse整個(gè)數(shù)組
    • 文字解釋有些難以理解,實(shí)際情況用實(shí)例將全排列的樹畫出來就能理解
  • Code:
    # code block
    public void nextPermutation(int[] nums) {
        if (nums == null || nums.length == 0) {
            return;
        }
        for (int i = nums.length - 2; i >= 0; i--) {
            if (nums[i] >= nums[i + 1]) {
                continue;
            }
            for (int j = nums.length - 1; j > i; j--) {
                if (nums[j] > nums[i]) {
                    swap(nums, i, j);
                    reverse(nums, i + 1, nums.length - 1);
                    return;
                }
            }
        }
        reverse(nums, 0, nums.length - 1);
    }
    
    private void swap(int[] nums, int i, int j) {
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }
    
    private void reverse(int[] nums, int start, int end) {
        while (start < end) {
            swap(nums, start, end);
            start++;
            end--;
        }
    }
    
  • Time Complexity: O(n)
    • 看似是兩重循環(huán)嵌套一個(gè)reverse,實(shí)際上第一個(gè)循環(huán)找到要交換的第一個(gè)點(diǎn),第二重循環(huán)找到要交換的第二個(gè)點(diǎn),對(duì)兩點(diǎn)間進(jìn)行reverse,實(shí)際是三個(gè)O(n)的操作
  • Space Complexity: O(1)

<center>#33 Search in Rotated Sorted Array</center>

  • link
  • Description:
    • Suppose an array sorted in ascending order 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).
    • You are given a target value to search. If found in the array return its index, otherwise return -1.
  • Input: 4 5 6 7 0 1 2, 5
  • Output: 2
  • Assumptions:
    • You may assume no duplicate exists in the array.
  • Solution:
    • 排序非重復(fù)rotated數(shù)組,滿足二分條件
    • 本題難點(diǎn)在于分情況討論
  • Code:
    # code block
    public int search(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int start = 0, end = nums.length - 1;
        while (start < end - 1) {
            int mid = start + (end - start) / 2;
            if (nums[mid] == target) {
                return mid;
            }
            if (nums[start] < nums[end]) {
                // mono increasing
                if (nums[mid] < target) {
                    start = mid;
                } else {
                    end = mid;
                }
            } else if (nums[mid] <= nums[end]) {
                if (nums[mid] < target && nums[end] >= target) {
                    start = mid;
                } else {
                    end = mid;
                }
            } else {
                if (nums[mid] > target && nums[start] <= target) {
                    end = mid;
                } else {
                    start = mid;
                }
            }
        }
        if (nums[start] == target) {
            return start;
        }
        if (nums[end] == target) {
            return end;
        }
        return -1;
    }
    
  • Time Complexity: O(lg n)
  • Space Complexity: O(1)

<center>#34 Search for a Range</center>

  • link
  • Description:
    • Given an array of integers sorted in ascending order, find the starting and ending position of a given target value.
    • Your algorithm's runtime complexity must be in the order of O(log n).
  • Input: [5, 7, 7, 8, 8, 10] and target value 8
  • Output: [3, 4]
  • Assumptions:
    • If the target is not found in the array, return [-1, -1]
  • Solution:
    • 使用兩次二分分別找target第一次出現(xiàn)的索引和最后一次出現(xiàn)的索引。
    • 使用兩次二分是由于worst case 整個(gè)數(shù)組都是target, 兩次二分能保證O(lg n)的復(fù)雜度。
  • Code:
    # code block
    public int[] searchRange(int[] nums, int target) {
        int[] result = new int[2];
        if (nums == null || nums.length == 0) {
            result[0] = -1;
            result[1] = -1;
            return result;
        }
        int start = 0, end = nums.length - 1;
        // find the first occurence of target
        while (start < end - 1) {
            int mid = start + (end - start) / 2;
            if (nums[mid] >= target) {
                end = mid;
            } else {
                start = mid;
            }
        }
        if (nums[start] == target) {
            result[0] = start;
        } else if (nums[end] == target) {
            result[0] = end;
        } else {
            result[0] = -1;
            result[1] = -1;
            return result;
        }
    
        start = 0;
        end = nums.length - 1;
        // find the last occurence of target
        while (start < end - 1) {
            int mid = start + (end - start) / 2;
            if (nums[mid] <= target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        if (nums[end] == target) {
            result[1] = end;
        } else {
            result[1] = start;
        }
        return result;
    }
    
    
  • Time Complexity: O(lg n)
  • Space Complexity: O(1)

<center>#35 Search Insert Position</center>

  • link

  • Description:

    • 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.
  • Input: [1,3,5,6], 5

  • Output: 2

  • Assumptions:

    • assume no duplicates in the array.
  • Solution:

    • 搜索排序數(shù)組就要想到二分法。 這題要找的是第一個(gè)>= target的索引。
    • 注意點(diǎn):
      • 使用start < end - 1 是防止棧溢出, 如果start = end - 1的話mid就會(huì)是start,如果nums[mid] < target就會(huì)陷入死循環(huán)
      • 從循環(huán)跳出會(huì)有兩種情況, start == end 或 start == end - 1。 所以對(duì)start和end進(jìn)行分別處理。
  • Code:

    # code block
    public int searchInsert(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int start = 0, end = nums.length - 1;
        while (start < end - 1) {
            int mid = start + (end - start) / 2;
            if (nums[mid] >= target) {
                end = mid;
            } else {
                start = mid;
            }
        }
        if (nums[start] >= target) {
            return start;
        } else if (nums[end] >= target) {
            return end;
        } else {
          // the target is greater than all the numbers in the array
            return end + 1;
        }
    }
    
    
  • Time Complexity: O(lg n)

  • Space Complexity: O(1)

<center>#36 Valid Sudoku</center>

  • link
  • Description:
    • 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 '.'.
  • Input:
  • Output:
  • Assumptions:
    • A valid Sudoku board (partially filled) is not necessarily solvable. Only the filled cells need to be validated.
  • Solution:
    • 注意邏輯要分離
  • Code:
    # code block
    public boolean isValidSudoku(char[][] board) {
        return checkRow(board) && checkColumn(board) && checkGrids(board);
    }
    
    private boolean checkRow(char[][] board) {
        for (int i = 0; i < 9; i++) {
            boolean[] visit = new boolean[10];
            for (int j = 0; j < 9; j++) {
                if (board[i][j] == '.') {
                    continue;
                }
                if (visit[board[i][j] - '0']) {
                    return false;
                }
                visit[board[i][j] - '0'] = true;
            }
        }
        return true;
    }
    
    private boolean checkColumn(char[][] board) {
        for (int j = 0; j < 9; j++) {
            boolean[] visit = new boolean[10];
            for (int i = 0; i < 9; i++) {
                if (board[i][j] == '.') {
                    continue;
                }
                if (visit[board[i][j] - '0']) {
                    return false;
                }
                visit[board[i][j] - '0'] = true;
            }
        }
        return true;
    }
    
    private boolean checkGrids(char[][] board) {
        for (int i = 0; i < 9; i += 3) {
            for (int j = 0; j < 9; j += 3) {
                if (!checkGrid(board, i, j)) {
                    return false;
                }
            }
        }
        return true;
    }
    
    private boolean checkGrid(char[][] board, int x, int y) {
        boolean[] visit = new boolean[10];
        for (int i = x; i < x + 3; i++) {
            for (int j = y; j < y + 3; j++) {
                if (board[i][j] == '.') {
                    continue;
                }
                if (visit[board[i][j] - '0']) {
                    return false;
                }
                visit[board[i][j] - '0'] = true;
            }
        }
        return true;
    }
    
  • Time Complexity: O(n ^ 2)
  • Space Complexity: O(n ^ 2)

<center>#38 Count and Say</center>

  • link
  • Description:
    • The count-and-say sequence is the sequence of integers with the first five terms as following:
    • 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 term of the count-and-say sequence.
    • Note: Each term of the sequence of integers will be represented as a string.
  • Input: 4
  • Output: "1211"
  • Solution:
    • 模擬題,按照題目的原意模擬過程
    • 注意corner case,比如1的時(shí)候
  • Code:
    # code block
    public String countAndSay(int n) {
        String result = "1";
        for (int i = 2; i <= n; i++) {
            result = countAndSay(result);
        }
        return result;
    }
    
    private String countAndSay(String str) {
        int count = 1;
        char say = str.charAt(0);
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i < str.length(); i++) {
            if (str.charAt(i) == say) {
                count++;
                continue;
            } else {
                sb.append(count);
                sb.append(say);
                count = 1;
                say = str.charAt(i);
            }
        }
        sb.append(count);
        sb.append(say);
        return sb.toString();
    }
    

<center>#39 Combination Sum</center>

  • link

  • Description:

    • Given a set of candidate numbers (C) (without duplicates) 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.
  • Input: [2, 3, 6, 7]

  • Output: [[7], [2, 2, 3]]

  • Assumptions:

    • without duplicates
      • All numbers (including target) will be positive integers.
  • Solution:

    • 組合的題, 不包含重復(fù)數(shù)字,可以把組合當(dāng)做一個(gè)隱式圖, 用DFS對(duì)隱式圖進(jìn)行遍歷。首先排序,因?yàn)榕磐晷蛑蠓奖闳ブ兀?還方便對(duì)樹進(jìn)行剪枝。假如第i個(gè)數(shù)已經(jīng)大于目標(biāo),那后面的數(shù)一定也大于目標(biāo),不在答案中。
    • 注意點(diǎn):
      • 每找到一個(gè)答案要進(jìn)行deepcopy,因?yàn)閐fs中用到了回溯,state數(shù)組會(huì)一直變化。
  • Code:

    # code block
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        if (candidates == null || candidates.length == 0) {
            return result;
        }
        Arrays.sort(candidates);
        dfsHelper(candidates, target, 0, new ArrayList<Integer>(), result);
        return result;
    }
    
    private void dfsHelper(int[] nums, int target, int start, List<Integer> state, List<List<Integer>> result) {
        if (target == 0) {
            result.add(new ArrayList<Integer>(state));
            return;
        }
        for (int i = start; i < nums.length; i++) {
          // 剪枝
            if (nums[i] > target) {
                break;
            }
            state.add(nums[i]);
            // start from i because every number can be used for multiple times.
            dfsHelper(nums, target - nums[i], i, state, result);
            // backtracking
            state.remove(state.size() - 1);
        }
    }
    
    

<center>#40 Combination Sum II</center>

  • link

  • Description:

    • 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.
  • Input: [10, 1, 2, 7, 6, 1, 5] and target 8

  • Output: [[1, 7], [1, 2, 5], [2, 6], [1, 1, 6]]

  • Assumptions:

    • All numbers (including target) will be positive integers.
      • The solution set must not contain duplicate combinations
  • Solution:

    • 求組合的題, 可重復(fù)數(shù)字,并且數(shù)組中每個(gè)數(shù)字只能用一次,可以把組合當(dāng)做一個(gè)隱式圖, 用DFS對(duì)隱式圖進(jìn)行遍歷。首先排序,因?yàn)榕磐晷蛑蠓奖闳ブ兀?還方便對(duì)樹進(jìn)行剪枝。假如第i個(gè)數(shù)已經(jīng)大于目標(biāo),那后面的數(shù)一定也大于目標(biāo),不在答案中。
    • 注意點(diǎn):
      • 每找到一個(gè)答案要進(jìn)行deepcopy,因?yàn)閐fs中用到了回溯,state數(shù)組會(huì)一直變化。
        • 去重方法用remove duplicate注釋了
  • Code:

    # code block
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
         List<List<Integer>> result = new ArrayList<>();
         if (candidates == null || candidates.length == 0) {
             return result;
         }
         Arrays.sort(candidates);
         dfsHelper(candidates, target, 0, new ArrayList<Integer>(), result);
         return result;
     }
    
     private void dfsHelper(int[] candidates, int target, int start, List<Integer> state, List<List<Integer>> result) {
         if (target == 0) {
             result.add(new ArrayList<Integer>(state));
             return;
         }
         for (int i = start; i < candidates.length; i++) {
             if (candidates[i] > target) {
                 break;
             }
             // remove duplicates
             if (i != start && candidates[i] == candidates[i - 1]) {
                 continue;
             }
             state.add(candidates[i]);
             dfsHelper(candidates, target - candidates[i], i + 1, state, result);
             state.remove(state.size() - 1);
         }
    
     }
    
    

<center>#43 Multiply Strings</center>

  • link
  • Description:
    • Given two non-negative integers num1 and num2 represented as strings, return the product of num1 and num2.
    • The length of both num1 and num2 is < 110.
    • Both num1 and num2 contains only digits 0-9.
    • Both num1 and num2 does not contain any leading zero.
    • You must not use any built-in BigInteger library or convert the inputs to integer directly.
  • Input: "123", "456"
  • Output: "56088"
  • Solution:
    • 暴力方法是直接模擬乘法計(jì)算
    • 比較優(yōu)雅的方法可以參考下圖
    • [圖片上傳失敗...(image-4f2751-1534568626309)]
    • 注意點(diǎn):主要處理trailing zeroes
  • Code:
    # code block
    public String multiply(String num1, String num2) {
        if (num1.equals("0") || num2.equals("0")) {
            return "0";
        }
        int l1 = num1.length();
        int l2 = num2.length();
        int[] result = new int[l1 + l2];
        for (int i = l1 - 1; i >= 0; i--) {
            for (int j = l2 - 1; j >= 0; j--) {
                int val = (num1.charAt(i) - '0') * (num2.charAt(j) - '0');
                int sum = result[i + j + 1] + val;
                result[i + j] += sum / 10;
                result[i + j + 1] = sum % 10;
            }
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < result.length; i++) {
            if (sb.length() == 0 && result[i] == 0) {
                continue;
            }
            sb.append(result[i]);
        }
        return sb.length() == 0 ? "0" : sb.toString();
    }
    
  • Time Complexity: O(m * n)
  • Space Complexity: O(m * n)

<center>#46 Permutations</center>

  • link
  • Description:
    • Given a collection of distinct numbers, return all possible permutations.
  • Input:[1,2,3]
  • Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
  • Solution:
    • 與排列相比較,需要多一個(gè)Set來記錄當(dāng)前狀態(tài)的值的索引
    • 回溯的時(shí)候注意不僅要回溯當(dāng)前狀態(tài),也需要回溯Set的值
    • 因?yàn)槭莇istinct numbers, 所以不需要去重
  • Code:
    # code block
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        if (nums == null || nums.length == 0) {
            return result;
        }
        dfsHelper(nums, new HashSet<Integer>(), new ArrayList<Integer>(), result);
        return result;
    }
    
    private void dfsHelper(int[] nums, Set<Integer> set, List<Integer> state, List<List<Integer>> result) {
        if (nums.length == set.size()) {
            result.add(new ArrayList<Integer>(state));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            if (!set.contains(i)) {
                set.add(i);
                state.add(nums[i]);
                dfsHelper(nums, set, state, result);
                // backtracking
                state.remove(state.size() - 1);
                set.remove(i);
            }
        }
    }
    

<center>#47 Permutations II</center>

  • link
  • Description:
    • Given a collection of numbers that might contain duplicates, return all possible unique permutations.
  • Input: [1,1,2]
  • Output: [[1,1,2],[1,2,1],[2,1,1]]
  • Solution:
    • Permutation 的 follow up,經(jīng)典的去重題
    • 與combination一樣,要選代表,不同的就是combination是通過start記錄歷史,而permutation是使用set
    • 去重第一個(gè)要想到排序
    • 去重的部分使用注釋標(biāo)識(shí)了
  • Code:
    # code block
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        if (nums == null || nums.length == 0) {
            return result;
        }
        Arrays.sort(nums);
        dfsHelper(nums, new HashSet<Integer>(), new ArrayList<Integer>(), result);
        return result;
    }
    
    private void dfsHelper(int[] nums, Set<Integer> set, List<Integer> state, List<List<Integer>> result) {
        if (nums.length == set.size()) {
            result.add(new ArrayList<Integer>(state));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            // remove duplicate
            if (set.contains(i)) {
                continue;
            }
            // if nums[i] equals to nums[i - 1], then nums[i - 1] must be in the permutation
            // this is the strategy for removing duplicates
            if (i != 0 && nums[i] == nums[i - 1] && !set.contains(i - 1)) {
                continue;
            }
    
            state.add(nums[i]);
            set.add(i);
            dfsHelper(nums, set, state, result);
            // backtracking
            set.remove(i);
            state.remove(state.size() - 1);
        }
    }
    

<center>#48 Rotate Image</center>

  • link
  • Description:
    • You are given an n x n 2D matrix representing an image.
    • Rotate the image by 90 degrees (clockwise).
    • You have to rotate the image in-place, which means you have to modify the input 2D matrix directly.
    • DO NOT allocate another 2D matrix and do the rotation.
  • Input:
    [
      [1,2,3],
      [4,5,6],
      [7,8,9]
    ],
    
  • Output:
    [
      [7,4,1],
      [8,5,2],
      [9,6,3]
    ]
    
  • Solution:
    • 這其實(shí)更像是找規(guī)律的題
    • 解法一:沿對(duì)角線翻轉(zhuǎn),按行reverse
    • 解法二:通過尋找規(guī)律可以發(fā)現(xiàn),swap三次可以使沿中心對(duì)稱的四個(gè)點(diǎn)到達(dá)最終位置,所以可以一圈一圈的轉(zhuǎn)換。One Pass。
  • Code:
    # code block
    解法一:
    public void rotate(int[][] matrix) {
        if (matrix == null || matrix.length <= 1) {
            return;
        }
        int n = matrix.length;
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < i; j++) {
                swap(matrix, i, j, j, i);
            }
        }
        for (int i = 0; i < n; i++) {
            reverse(matrix, i);
        }
    }
    
    private void reverse(int[][] matrix, int x) {
        int start = 0, end = matrix[x].length - 1;
        while (start < end) {
            int tmp = matrix[x][start];
            matrix[x][start] = matrix[x][end];
            matrix[x][end] = tmp;
            start++;
            end--;
        }
    }
    
    private void swap(int[][] matrix, int x1, int y1, int x2, int y2) {
        int tmp = matrix[x1][y1];
        matrix[x1][y1] = matrix[x2][y2];
        matrix[x2][y2] = tmp;
    }
    解法二:
    public void rotate(int[][] matrix) {
        if (matrix == null || matrix.length <= 1) {
            return;
        }
        int n = matrix.length;
        int start = 0, end = n - 1;
        while (start < end) {
            for (int i = start; i < end; i++) {
                swap(matrix, start, i, i, end);
                swap(matrix, start, i, end, end - i);
                swap(matrix, start, i, end - i, start);
            }
            start++;
            end--;
        }
    }
    
    private void swap(int[][] matrix, int x1, int y1, int x2, int y2) {
        int tmp = matrix[x1][y1];
        matrix[x1][y1] = matrix[x2][y2];
        matrix[x2][y2] = tmp;
    }
    
  • Time Complexity: O(n ^ 2)
  • Space Complexity: O(1)

<center>#49 Group Anagrams</center>

  • link
  • Description:
    • Given an array of strings, group anagrams together.
  • Input: ["eat","tea","tan","ate","nat","bat"]
  • Output: [["bat"],["ate","eat","tea"],["nat","tan"]]
  • Solution:
    • 這題考察的是哈希表的應(yīng)用,以及對(duì)anagram的理解
    • 通過對(duì)每一個(gè)string的char數(shù)組排序作為key,每一組anagram都有同一個(gè)key,使用hashmap做收集
    • 更優(yōu)的解法是自己寫不重復(fù)的hash function,可以參考網(wǎng)上的質(zhì)數(shù)解法
  • Code:
    # code block
    public List<List<String>> groupAnagrams(String[] strs) {
        List<List<String>> result = new ArrayList<>();
        if (strs == null || strs.length == 0) {
            return result;
        }
        Map<String, List<String>> map = new HashMap<>();
        for (String s : strs) {
            char[] sc = s.toCharArray();
            Arrays.sort(sc);
            String key = new String(sc);
            if (!map.containsKey(key)) {
                map.put(key, new ArrayList<String>());
            }
            map.get(key).add(s);
        }
        for (String key : map.keySet()) {
            result.add(map.get(key));
        }
        return result;
    }
    
  • Time Complexity: O(n * lg(length))
  • Space Complexity: O(n * length)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,854評(píng)論 0 10
  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 14,246評(píng)論 0 38
  • 如今洗澡是一種享受,當(dāng)年洗澡是一道風(fēng)景。 那年,學(xué)校剛建不久,條件簡陋,澡堂小,自來水也供應(yīng)不足,夏天要洗個(gè)澡,要...
    沐芝陽閱讀 821評(píng)論 1 2
  • 歷史往往蘊(yùn)含著活生生的經(jīng)濟(jì)案例和未來,自遠(yuǎn)古人類就誕生的基于血緣和地緣關(guān)系的“氏族公社”,如今正以現(xiàn)代新型“社群”...
    嘟嘟飛天閱讀 514評(píng)論 0 0
  • 如果讓我去做服務(wù),就是這種感覺 草木無情 我也無情 很難從一件事轉(zhuǎn)換到另一件事 服務(wù) 就是被拒絕 被拒絕意味著痛哭...
    這只動(dòng)物有點(diǎn)冷閱讀 643評(píng)論 0 0

友情鏈接更多精彩內(nèi)容