力扣刷題總結


刷題總結

1,count數組

#include <iostream>
#include <unordered_map>
using namespace std;
// 輸入: 11223344455
// 輸出:{1:2, 2: 2, 3: 2, 4: 3, 5: 1}
void printNums(vector<int> nums) {
    for(int i = 0; i < nums.size(); i++) {
        cout << nums[i] << '\t';
    }
}

vector<pair<int,int>> analysis(vector<int> nums) {
    unordered_map<int, int> mp;
    for(int i = 0; i < nums.size(); i++) {
        if(mp.find(nums[i]) == mp.end()) {
            mp.insert(pair(nums[i], 1));
        } else {
            mp[nums[i]] = mp.find(nums[i])->second + 1;
        }
    }
    vector<pair<int, int>> result(mp.begin(), mp.end());
    sort(result.begin(), result.end(), [](pair<int,int>&a, pair<int, int>&b) {
        return a.second < b.second;
    });
    return result;
}

void printPair(vector<pair<int, int>> &nums) {
    for(int i = 0; i < nums.size(); i++) {
        cout << nums[i].first << ": " << nums[i].second << "\t";
    }
}
int main() {
    vector<int> nums = {7, 7, 2, 2, 3, 3, 4, 4 , 4, 5, 5};
    // printNums(nums);
    vector<pair<int, int>> result = analysis(nums);
    printPair(result);
    
}

2, leetcode 1. 兩數之和

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> mp = new HashMap<>();
        for(int i = 0; i < nums.length; i++) {
            if(mp.containsKey(target - nums[i])) {
                return new int[] {i, mp.get(target-nums[i])};
            }
            mp.put(nums[i], i);
        }
        return new int[2];
    }
}


3,leetcode 4.尋找兩個有序數組的中位數

解法一:

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        // 合並兩個數組
        ArrayList<Integer> array = new ArrayList<>();
        int i = 0, j = 0;
        while(i < nums1.length || j < nums2.length) {
            if(i < nums1.length && j < nums2.length && nums1[i] < nums2[j]) {
                array.add(nums1[i++]);
            } else if(j < nums2.length && i < nums1.length && nums1[i] >= nums2[j]) {
                array.add(nums2[j++]);
            }else if (j < nums2.length) {
                array.add(nums2[j++]);
            } else if (i < nums1.length) {
                array.add(nums1[i++]);
            }
        }
        int totalLength = nums1.length + nums2.length;
        int middle = (totalLength - 1) / 2;
        if(totalLength % 2 == 0) {
            return (array.get(middle) + array.get(middle+1) )/ 2.0;
        } else {
            return array.get(middle);
        }
    }
}

解法二:

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m = nums1.length;
        int n = nums2.length;
        if(m > n) {
            return findMedianSortedArrays(nums2, nums1);
        }

        int c1 = 0, c2 = 0, lo = 0, hi = 2*m, LMAX1 = 0, LMAX2 = 0, RMIN1 = 0, RMIN2 = 0;
        while(lo <= hi) {
            c1 = (lo + hi) / 2;
            c2 = m + n - c1;

            LMAX1 = (c1 == 0) ? Integer.MIN_VALUE : nums1[(c1-1)/2];
            LMAX2 = (c2 == 0) ? Integer.MIN_VALUE : nums2[(c2-1)/2];
            RMIN1 = (c1 == 2*m) ? Integer.MAX_VALUE : nums1[c1/2];
            RMIN2 = (c2 == 2*n) ? Integer.MAX_VALUE : nums2[c2/2];
            
            if(LMAX1 > RMIN2) {
                hi = c1 - 1;
            } else if(LMAX2 > RMIN1) {
                lo = c1 + 1;
            } else {
                break;
            }
        }
        return (Math.max(LMAX1, LMAX2) + Math.min(RMIN1, RMIN2)) / 2.0;
    }
}

4,leetcode 11.盛水最多的容器

解法一(暴力)

class Solution {
    public int maxArea(int[] height) {
        int max = Integer.MIN_VALUE;
        int len = height.length;
        int tmp = 0;
        for(int i = 0; i < len - 1; i++) {
            for (int j = i + 1; j < len; j++) {
                tmp = (j - i)*(Math.min(height[i], height[j]));
                if(tmp > max) {
                    max = tmp;
                }
            }
        }
        return max;
    }
}

解法二(雙指針)

class Solution {
    public int maxArea(int[] height) {
        int res = Integer.MIN_VALUE;
        int i = 0, j = height.length - 1;
        while(i < j) {
            if(height[i] < height[j]){
                res = Math.max(res, height[i]*(j - i));
                i++;
            } else {
                res = Math.max(res, height[j]*(j - i));
                j--;
            }
        }
        return res;
    }
}

5, leetcode 15. 三數之和

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList();
        int len = nums.length;
        if(len < 3) return result;
        Arrays.sort(nums);
        for(int i = 0; i < len; i++) {
            if(nums[i] > 0) break;
            if(i > 0 && nums[i] == nums[i-1]) continue;
            int L = i+1, R = len - 1;
            while(L < R) {
                int sum = nums[i] + nums[L] + nums[R];
                if(sum == 0) {
                    result.add(Arrays.asList(nums[i], nums[L], nums[R]));
                    while(L < R && nums[L] == nums[L+1]) L++;
                    while(L < R && nums[R] == nums[R-1]) R--;
                    L++;
                    R--;
                }
               
                if(sum > 0) R--;
                if(sum < 0) L++;
            }
        }
        return result;
    }
}

6, leetcode 16. 最接近三數之和

class Solution {
    public int threeSumClosest(int[] nums, int target) {
        int len = nums.length;
        if(len < 3) return 0;
        int result = nums[0] + nums[1] + nums[2];
        Arrays.sort(nums);
        for(int i = 0; i < len; i++) {
            int L = i + 1;
            int R = len - 1;
            while(L < R) {
                int sum = nums[i] + nums[L] + nums[R];
                result = Math.abs(result - target) > Math.abs(sum - target) ? sum : result;
                if(sum > target) {
                    R--;
                }else if(sum < target) {
                    L++;
                }else {
                    return target;
                }
                
            }
        }
        return result;
    }
}

7, leetcode 18. 四數之和

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> result = new ArrayList();
        int len = nums.length;
        if(len < 4) return result;
        Arrays.sort(nums);
        for(int i = 0; i <= len - 4; i++) {
            if(i > 0 && nums[i] == nums[i-1]) continue;
            for(int j = i+1; j <= len - 3; j++) {
                if(j > i+1 && nums[j] == nums[j-1]) continue;
                int l = j+1, r = len - 1;
                while(l < r) {
                    int sum = nums[i] + nums[j] + nums[l] + nums[r];
                    if(sum > target) {
                        r--;
                    }else if(sum < target) {
                        l++;
                    }else {
                        result.add(Arrays.asList(nums[i], nums[j], nums[l], nums[r]));
                        while(nums[l]==nums[l+1]) l++;
                        while(nums[r] == nums[r-1]) r--;
                        l++;
                        r--;
                    }
                }
            }
        }
        return result;
    }
}

8. leetcode 26. 刪除排序數組中的重復項

解法一:(暴力移動法)

class Solution {
    public int removeDuplicates(int[] nums) {
        //判斷重復 記錄位置 刪除
        int end = nums.length, i = 0;
        while(i < end) {
            if(i > 0 && nums[i] == nums[i-1]) {
                //移動
                for(int j = i+1; j < end; j++) {
                    nums[j-1] = nums[j];
                }
                end--;
            }else {
                i++;
            }
        }
        return end;
    }
}

解法二:(雙指針法)

class Solution {
    public int removeDuplicates(int[] nums) {
        int len = nums.length;
        if(len == 0) return 0;
        else if(len == 1) return 1;
        else {
            int i = 0;
            for(int j = 1; j < len; j++) {
                if(nums[i] != nums[j]) {
                    i++;
                    nums[i] = nums[j];
                }
            }
            return i+1;
        }
    }
}

9. leetcode 3. 無重復字符的最長字串

解法一:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int len = s.length();
        int i = 0;
        int result = 0;
        while(i < len) {
            Set<String> set= new HashSet();
            int j = 0;
            for(j = i; j < len; j++) {
                if(!set.contains(s.charAt(j)+"")) {
                    result = Math.max(result, j-i+1);
                }else {
                    break;
                }
                set.add(s.charAt(j)+"");
            }
            i++;
        }
        return result;
    }
}

解法二:(滑動窗口)

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int LeftIndex = 0;
        int result = 0;
        for(int j = 0; j < s.length(); j++) {
            for(int CurIndex = LeftIndex; CurIndex < j; CurIndex++) {
                if(s.charAt(CurIndex) == s.charAt(j)) {
                    result = Math.max(result, j - LeftIndex);
                    LeftIndex = CurIndex + 1;
                    break;
                }
            }
        }
        return Math.max(s.length()-LeftIndex, result);
    }
}

10. leetcode 27. 移除元素

解法一:

class Solution {
    public int removeElement(int[] nums, int val) {
        int i = 0;
        for(int j = 0; j < nums.length; j++) {
            if(nums[j] != val) {
                nums[i] = nums[j];
                i++;
            }
        }
        return i;
    }
}

解法二:

class Solution {
    public int removeElement(int[] nums, int val) {
        int len = nums.length;
        int i = 0;
        while(i < len) {
            if(nums[i] == val) {
                nums[i] = nums[len-1];
                len--;
            }else {
                i++;
            }
        }
        return len;
    }
}

11. leetcode 6. Z字形變換

解法一:

class Solution {
    public String convert(String s, int numRows) {
        int len = s.length();
        //根據s的長度 和numRows來分配初始數組的大小
        StringBuilder sb = new StringBuilder();
        if(len == 0) return sb.toString();
        if(numRows == 1) return s;
        
        int numCols = (len / (2*numRows - 2) + 1) * (numRows-1);
        char[][] result = new char[numRows][numCols];
        int i = 0, row = 0, col = 0;
        while(i < len) {
            for(int j = 0; j < numRows && i < len; j++) {
                result[j][col] = s.charAt(i++);
            }
            col++;
            for(int j = numRows-2; j >= 1 && i < len; j--) {
                result[j][col++] = s.charAt(i++);
            }
        }
        for(i = 0; i < result.length; i++) {
            for(int j = 0; j < result[0].length; j++) {
                if(result[i][j] != '\0') {
                    sb.append(result[i][j]);
                }
            }
        }
        return sb.toString();

    }
}

解法二:

class Solution {
    public String convert(String s, int numRows) {
        int len = s.length();
        numRows = Math.min(numRows, len);
        if(numRows == 1) return s;
        //構造stringbuilder
        List<StringBuilder> sb = new ArrayList<>();
        for(int i = 0; i < numRows; i++) {
            sb.add(new StringBuilder());
        }
        boolean goDown = false;
        int row = 0;
        for(int i = 0; i < len; i++) {
            sb.get(row).append(s.charAt(i));
            if(row == 0 || row == numRows - 1) goDown = !goDown;
            row += goDown ? 1 : -1;
        }

        StringBuilder res = new StringBuilder();
        for(int i = 0; i < sb.size(); i++) {
            res.append(sb.get(i));
        }
        return res.toString();
    }
}

12. leetcode 5. 最長回文子串

解法一:(暴力)

class Solution {
    public String longestPalindrome(String s) {
        if(s.length() < 2) return s;
        String res = s.substring(0, 1);
        int maxLen = 1;
        for(int i = 0; i < s.length() - 1; i++) {
            for(int j = i + 1; j < s.length(); j++) {
                if(j-i+1 > maxLen && valid(i, j, s)) {
                    maxLen = j - i + 1;
                    res = s.substring(i, j+1);
                }
            }
        }
        return res;
    }

    public boolean valid(int i, int j, String s) {
        char[] ss = s.toCharArray();
        while(i <= j) {
            if(ss[i++] != ss[j--]) {
                return false;
            }
        }
        return true;
    }
}

解法二:(中心擴散法)

class Solution {
    public String longestPalindrome(String s) {
        int len = s.length();
        if(len < 2) return s;
        String res = s.substring(0, 1);
        int maxLen = 1;
        for(int i = 0; i < len-1; i++) {
            String s1 = centerS(s, i, i);
            String s2 = centerS(s, i, i+1);
            String maxS = s1.length() > s2.length() ? s1 : s2;
            if(maxS.length() > maxLen) {
                maxLen = maxS.length();
                res = maxS;
            }
        }
        return res;
    }

    public String centerS(String s, int l, int r) {
        int len = s.length();
        while(l >= 0 && r < len) {
            if(s.charAt(l) == s.charAt(r)) {
                l--;
                r++;
            } else {
                break;
            }
        }
        return s.substring(l+1, r);
    }
}

解法三:(動態規划)

class Solution {
    public String longestPalindrome(String s) {
        int len = s.length();
        if(len < 2) return s;
        boolean[][] dp = new boolean[len][len];
        String res = s.substring(0, 1);
        int maxLen = 1;
        for(int r = 1; r < len; r++) {
            for(int l = 0; l < r ; l++) {
                if(s.charAt(l) == s.charAt(r) && (r - l <= 2 || dp[l+1][r-1])) {
                    dp[l][r] = true;
                    if(r - l + 1 > maxLen) {
                        maxLen = r - l + 1;
                        res = s.substring(l, r+1);
                    }
                }
            }
        }
        return res;
    }
}

13. leetcode 102. 二叉樹的層次遍歷

解法一:(遞歸)

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public static  List<List<Integer>> res;
    public List<List<Integer>> levelOrder(TreeNode root) {
        res = new ArrayList<>();
        if(root == null) return res;
        helper(root, 0);
        return res;
    }

    public void helper(TreeNode root, int level) {
        if(res.size() == level) {
            res.add(new ArrayList());
        }
        res.get(level).add(root.val);
        if(root.left != null) {
            helper(root.left, level+1);
        }
        if(root.right != null) {
            helper(root.right, level+1);
        }
    }
}

解法二:(迭代)

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if(root == null) return res;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()) {
            int _size = queue.size();
            List<Integer> tmp = new ArrayList<>();
            for(int i = 0; i < _size; i++) {
                TreeNode node = queue.remove();
                tmp.add(node.val);
                if(node.left != null) {
                    queue.offer(node.left);
                }
                if(node.right != null) {
                    queue.offer(node.right);
                }
            }
            res.add(tmp);
        }
        return res;
    }
}

14. leetcode 103. 二叉樹的鋸齒形層次遍歷

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if(root == null) return  res;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()) {
            int _size = queue.size();
            List<Integer> tmp = new ArrayList<>();
            for(int i = 0; i < _size; i++) {
                TreeNode node = queue.remove();
                tmp.add(node.val);
                if(node.left != null){
                    queue.offer(node.left);
                }
                if(node.right != null) {
                    queue.offer(node.right);
                }
            }
            if(res.size()%2 == 0) {
                res.add(tmp);
            } else {
                Collections.reverse(tmp);
                res.add(tmp);
            }
        }
        return res;
    }
}

15, leetcode 100.相同的樹

解法一:(遞歸)

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if(p == null && q == null) return true;
        else if(p == null || q == null) return false;
        else {
            if(p.val != q.val) return false;
        }
        return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
    }
}

樹的框架套路

nextT hexo主題

16, leetcode 96. 不同的二叉搜索樹

class Solution {
    public int numTrees(int n) {
        //動態規划 , 轉移函數通過找規律
        //dp[i] += dp[i-1]*dp[n-i]
        int[] dp = new int[n+1];
        dp[0] = 1;
        dp[1] = 1;
        for(int i = 2; i < n+1; i++) {
            for(int j = 1; j <= i; j++) {
                dp[i] += dp[j-1]*dp[i-j];
            }
        }
        return dp[n];
    }
}

17, leetcode 8. 字符串轉整數(atoi)

解法一(發生越界):

class Solution {
    public int myAtoi(String str) {
        int len = str.length();
        if(len == 0) return 0;
        int start = findFirst(str);
        if(start == len) return 0;
        if(str.charAt(start) == '+') {
            return cal(str, start+1);
        } else if (str.charAt(start) == '-') {
            return 0 - cal(str, start+1);
        } else {
            return cal(str, start);
        }
    }

    public int cal(String str, int start) {
        int res = 0;
        int len = str.length();
        for(int i = start; i < len; i++) {
            char c = str.charAt(i);
            if(isNum(c)) {
                res += (c - '0');
                res *= 10;                    
            } else {
                break;
            }
        }
        return res/10;
    }

    public int findFirst(String str) {
        int len = str.length();
        int i = 0;
        while(i < len) {
            if(str.charAt(i) == '+' || str.charAt(i) == '-' || (str.charAt(i) >= '0' && str.charAt(i) <= '9')) {
                return i;
            } else if (str.charAt(i) != ' ') {
                return len;
            }
            i++;
        }
        return i;
    }

    public boolean isNum(char c) {
        if(c >= '0' && c <= '9') {
            return true;
        } 
        return false;
    }
}

改進:

class Solution {
    public boolean flag = true;
    public int myAtoi(String str) {
        int len = str.length();
        if(len == 0) return 0;
        int start = findFirst(str);
        if(start == len) return 0;
        return cal(str, start);
    }

    public int cal(String str, int start) {
        int res = 0;
        int len = str.length();
        for(int i = start; i < len; i++) {
            char c = str.charAt(i);
            if(isNum(c)) {
                res = res*10 + (c - '0');
                if( flag && i+1 < len && isNum(str.charAt(i+1)) &&( res > Integer.MAX_VALUE / 10 ||res == Integer.MAX_VALUE/10 && str.charAt(i+1) - '0' > Integer.MAX_VALUE%10)) {
                    return Integer.MAX_VALUE ;
                }
                if(!flag && i+1 < len && isNum(str.charAt(i+1)) &&( -res < Integer.MIN_VALUE / 10 ||-res == Integer.MIN_VALUE/10 && -(str.charAt(i+1) - '0') < Integer.MIN_VALUE%10)) {
                    return Integer.MIN_VALUE ;
                }
            } else {
                break;
            }
        }
        return flag ? res : res*(-1);
    }

    public int findFirst(String str) {
        int len = str.length();
        int i = 0;
        while(i < len) {
            if(str.charAt(i) == '+') {
                flag = true;
                return i+1;
            }else if(str.charAt(i) == '-') {
                flag = false;
                return i+1;
            }else if (str.charAt(i) >= '0' && str.charAt(i) <= '9') {
                return i;
            } else if (str.charAt(i) != ' ') {
                return len;
            }
            i++;
        }
        return i;
    }

    public boolean isNum(char c) {
        if(c >= '0' && c <= '9') {
            return true;
        } 
        return false;
    }
}

解法二:(正則表達式):

class Solution(object):
    def myAtoi(self, str):
        """
        :type str: str
        :rtype: int
        """
        return max(min(int(*re.findall(r'^[\+|\-]?\d+', str.lstrip())), 2**31-1), -2**31)

這道題硬磕了一個小時,邊界情況還是含糊不清,需要集中注意力。加油!!

18, leetcode 13.羅馬數字轉整數

class Solution {
    public int romanToInt(String s) {
        int len = s.length();
        if(len == 0) return 0;
        int i = 0;
        int res = 0;
        while(i < len) {
            if(i < len && s.charAt(i) == 'M') {
                res += 1000;
                i++;
            }
            if(i < len && s.charAt(i) == 'D') {
                res += 500;
                i++;
            }
            if(i < len && s.charAt(i) == 'C' && (i+1 < len &&!(s.charAt(i+1) == 'D' || s.charAt(i+1) == 'M') || i+1 >= len)) {
                res += 100;
                i++;
            } else if (i < len && s.charAt(i) == 'C' && i+1 < len && (s.charAt(i+1) == 'D')) {
                res += 400;
                i += 2;
            } else if(i < len && s.charAt(i) == 'C' && i+1 < len && (s.charAt(i+1) == 'M')) {
                res += 900;
                i += 2;
            }
            if(i < len && s.charAt(i) == 'L') {
                res += 50;
                i++;
            }
            if (i < len && s.charAt(i) == 'X' && (i+1 < len &&!(s.charAt(i+1) == 'L' || s.charAt(i+1) == 'C') || i+1 >= len)) {
                res += 10;
                i++;
            } else if (i < len && s.charAt(i) == 'X' && i+1 < len && s.charAt(i+1) == 'L') {
                res += 40;
                i += 2;
            } else if(i < len && s.charAt(i) == 'X' && i+1 < len &&(s.charAt(i+1) == 'C')) {
                res += 90;
                i += 2;
            }
            if(i < len && s.charAt(i) == 'V') {
                res += 5;
                i++;
            }
            if(i < len && s.charAt(i) == 'I' &&( i+1 < len &&!(s.charAt(i+1) == 'V' || s.charAt(i+1) == 'X') || i+1 >= len)) {
                res += 1;
                i++;
            } else if(i < len && s.charAt(i) == 'I' && i+1 < len && s.charAt(i+1) == 'V') {
                res += 4;
                i += 2;
            } else if(i < len && s.charAt(i) == 'I' && i+1 < len && s.charAt(i+1) == 'X') {
                res += 9;
                i += 2;
            }

            
        }
        return res;
    }
}

這個解題方法很恐怖,首先邊界情況太多,還有很多重復的代碼塊,不好維護。全程if else,寫的也很耽誤時間,特別需要優化。

解法二:其實在寫以上題解過程中,也是想到要不要將值存到hashmap里面,這樣直接讀取就好了,無奈懶得更進一步去思考IV這種情況。就是直接去hashmap里面取值嘛,取不到就取單個字符的。

class Solution {
    public int romanToInt(String s) {
        Map<String, Integer> map = new HashMap<>();
        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);
        map.put("IV", 4);
        map.put("IX", 9);
        map.put("XL", 40);
        map.put("XC", 90);
        map.put("CD", 400);
        map.put("CM", 900);
        int len = s.length();
        if(len == 0) return 0;
        int i = 0;
        int res = 0;
        while(i < len) {
            if(i < len && i+2 <= len && map.containsKey(s.substring(i, i+2))) {
                res += map.get(s.substring(i, i+2));
                i += 2;
            }else if(i < len && map.containsKey(s.substring(i, i+1))) {
                res += map.get(s.substring(i, i+1));
                i +=1;
            }
        }
        return res;
    }
}

19,leetcode 14. 最長公共前綴

class Solution {
    public String longestCommonPrefix(String[] strs) {
        int index = 0;
        int len = strs.length;
        if(len < 1 ||(len >= 0 && strs[0].length() == 0)) return "";
        StringBuilder sb = new StringBuilder();
        boolean flag = true;
        while(flag && index < strs[0].length()) {
            char c = strs[0].charAt(index);
            for(int i = 1; i < len; i++) {
                if(index < strs[i].length() && strs[i].charAt(index) != c || index >= strs[i].length()) {
                    flag = false;
                    break;
                }
            }
            if (flag) sb.append(strs[0].charAt(index));
            index++;
        }
        return sb.toString();
    }
}

20, leetcode 12.整數轉羅馬數字

class Solution {
    public String intToRoman(int num) {
        String[] romans = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
        int[] nums = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        if(num <= 0) return "";
        StringBuilder sb = new StringBuilder();
        while(num > 0) {
            for(int i = 0; i < 13; i++) {
                if(num >= nums[i]) {
                    num -= nums[i];
                    sb.append(romans[i]);
                    break;
                }
            }
        }
        return sb.toString();
    }
}

21, leetcode 17. 電話號碼的字母組合

class Solution {
    public List<String> letterCombinations(String digits) {
        StringBuilder sb = new StringBuilder();
        int len = digits.length();
        if(len == 0) return result;
        backtrack("", digits);
        return result;
    }
    public List<String> result = new ArrayList<>();
    String[] mm = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
    public void backtrack(String combinations, String digits) {
        if(digits.length() == 0) {
            result.add(combinations);
            return;
        }else {
            String ss = mm[digits.charAt(0)-'2'];
            for(int i = 0; i < ss.length(); i++) {
                backtrack(combinations+ss.substring(i, i+1), digits.substring(1));
            }
        }
    }
}

22, leetcode 401. 二進制手表

class Solution {
    public List<String> readBinaryWatch(int num) {
        if(num == 0) {
            result.add("0:00");
            return result;
        }
        if(num > 0 && num <= 10) {
            backtrack(0, num);
        }
        return result;
    }
    public List<String> result = new ArrayList<>();
    public int[] time = {480, 240, 120, 60, 32, 16, 8, 4, 2, 1};
    public boolean[] visited = new boolean[10];
    public void backtrack(int combinations, int nums) {
        if(nums == 0) {
            int h = combinations / 60;
            int s = combinations % 60;
            String tmp = "";
            if(s < 10) {
                tmp = String.format("%d:0%d", h, s);
            } else {
                tmp = String.format("%d:%d", h, s);
            }
            result.add(tmp);
            return;
        } else {
            for(int i = 0; i < 10; i++) {
                if(!visited[i]) {
                    combinations += time[i];
                    visited[i] = true;
                    backtrack(combinations, nums-1);
                    combinations -= time[i];
                    visited[i] = false;
                }
            }
        }
    }
}

以上的解法不對,是因為,小時只能重用一次,分鍾只能重用一次,而不是小時和分鍾加起來只能重用一次。//不是, 每一次只能使用一個元素。

class Solution {
    public List<String> readBinaryWatch(int num) {
        if(num == 0) {
            result.add("0:00");
            return result;
        }
        if(num > 0 && num <= 10) {
            backtrack(0, num, 0);
        }
        return result;
    }
    public List<String> result = new ArrayList<>();
    public int[] time = {480, 240, 120, 60, 32, 16, 8, 4, 2, 1};
    public boolean[] visited = new boolean[10];
    public void backtrack(int combinations, int nums, int start) {
        int h = combinations / 60;
        int s = combinations % 60;
        if(nums == 0) {
           
            String tmp = "";
            if(s < 10) {
                tmp = String.format("%d:0%d", h, s);
            } else {
                tmp = String.format("%d:%d", h, s);
            }
            result.add(tmp);
            return;
        } else {
            for(int i = start; i < 10; i++) {
                if(i < 4 ) {
                        combinations += time[i];
                        if(h + time[i]/60 < 12)
                            backtrack(combinations, nums-1, i+1);
                        combinations -= time[i];
                }
                else {
                        combinations += time[i];
                        if(s + time[i] < 60) 
                            backtrack(combinations, nums-1, i+1);
                        combinations -= time[i];
                }
            }
        }
    }
}

23, leetcode 784. 字母大小寫全排列

class Solution {
    public List<String> letterCasePermutation(String S) {
        backtrack("", 0, S);
        return result;
    }
    public List<String> result = new ArrayList<>();
    public void backtrack(String combinations, int start, String nextString) {
        if(nextString.length() == start) {
            result.add(combinations);
            return;
        } else {
            for(int i = start; i < nextString.length(); i++) {
                String tmp = nextString.substring(i, i+1);
                String tt = transfer(tmp);
                backtrack(combinations+tt, start+1, nextString);
            }
        }
    }

    public String transfer(String tt) {
        String tmp = tt;
        if(tt.length() != 0) {
            char c = tt.charAt(0);
            if(Character.isUpperCase(c)) {
                tmp = tt.toLowerCase();
            } else if (Character.isLowerCase(c)) {
                tmp = tt.toUpperCase();
            }
        }
        return tmp;
    }
}

不知道以上的思路是什么玩意,到現在都沒想明白

得出了一下的輸出

輸入:"a1b2"

輸出:["A1B2","A122","ABB2","AB22","A2B2","A222","11B2","1122","1BB2","1B22","12B2","1222","B1B2","B122","BBB2","BB22","B2B2","B222","21B2","2122","2BB2","2B22","22B2","2222"]

預期結果:

["a1b2","a1B2","A1b2","A1B2"]

class Solution {
    public List<String> letterCasePermutation(String S) {
        Stack<Character> stack = new Stack<>();
        dfs(S, 0, stack);
        return res;
    }
    public List<String> res = new ArrayList<>();

    public void dfs(String S, int index, Stack<Character> stack) {
        if(S.length() == index) {
            StringBuilder sb = new StringBuilder();
            for(int i = 0; i < index; i++) {
                sb.append(stack.get(i));
            }
            res.add(sb.toString());
            return;
        }

        stack.add(S.charAt(index));
        dfs(S, index+1, stack);
        stack.pop();

        if(Character.isUpperCase(S.charAt(index))) {
            stack.add(S.substring(index, index+1).toLowerCase().charAt(0));
            dfs(S, index+1, stack);
            stack.pop();
        } else if(Character.isLowerCase(S.charAt(index))) {
            stack.add(S.substring(index, index+1).toUpperCase().charAt(0));
            dfs(S, index+1, stack);
            stack.pop();
        }
    }
}

遞回溯算法框架:

非遞歸:
 1: int a[n],i;
   2: 初始化數組a[];
   3: i = 1;
   4: while (i>0(有路可走)   and  (未達到目標))  // 還未回溯到頭
   5: {
   6:     if(i > n)                                              // 搜索到葉結點
   7:     {   
   8:           搜索到一個解,輸出;
   9:     }
  10:     else                                                   // 處理第i個元素
  11:     { 
  12:           a[i]第一個可能的值;
  13:           while(a[i]在不滿足約束條件且在搜索空間內)
  14:           {
  15:               a[i]下一個可能的值;
  16:           }
  17:           if(a[i]在搜索空間內)
  18:          {
  19:               標識占用的資源;
  20:               i = i+1;                              // 擴展下一個結點
  21:          }
  22:          else 
  23:         {
  24:               清理所占的狀態空間;            // 回溯
  25:               i = i –1; 
  26:          }
  27: }
遞歸:
   1: int a[n];
   2: try(int i)
   3: {
   4:     if(i>n)
   5:        輸出結果;
   6:      else
   7:     {
   8:        for(j = 下界; j <= 上界; j=j+1)  // 枚舉i所有可能的路徑
   9:        {
  10:            if(fun(j))                 // 滿足限界函數和約束條件
  11:              {
  12:                 a[i] = j;
  13:               ...                         // 其他操作
  14:                 try(i+1);
  15:               回溯前的清理工作(如a[i]置空值等);
  16:               }
  17:          }
  18:      }
  19: }

24. leetcode 78. 子集

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        Stack<Integer> stack = new Stack();
        backtrack(nums, 0, stack);
        return res;
    }

    public List<List<Integer>> res = new ArrayList();
    public void backtrack(int[] nums, int start, Stack<Integer> stack) {
            List<Integer> list = new ArrayList();
            for(int j = 0; j < stack.size(); j++) {
                list.add(stack.get(j));
            }
            res.add(list);
            if(start == nums.length) {
                return;
            } else {
                
                for(int i = start; i < nums.length; i++) {
                    stack.add(nums[i]);
                    backtrack(nums, i+1, stack);
                    stack.pop();
                }
            }
        
    }
}

25, leetcode 51. N皇后

class Solution {
    public List<List<String>> solveNQueens(int n) {
        Stack<Integer> stack = new Stack();
        boolean[] cols = new boolean[n];
        boolean[] master = new boolean[2*n];
        boolean[] slave = new boolean[2*n];
        if(n == 0) return res;
        backtrack(n, 0, stack, cols, master, slave);
        return res;
    }

    public List<List<String>> res = new ArrayList();


    public void backtrack(int n, int row, Stack<Integer> stack, boolean[] cols, boolean[] master, boolean[] slave) {
        if(row == n) {
            List<String> tmp = convert(stack, n);
            res.add(tmp);
            return;
        } else {
            for(int i = 0; i < n; i++) {
                if(!cols[i] && !master[row+i] && !slave[row-i+n-1]) {
                    stack.add(i);
                    cols[i] = true;
                    master[row+i] = true;
                    slave[row-i+n-1] = true;
                    backtrack(n, row+1, stack, cols, master, slave);
                    stack.pop();
                    cols[i] = false;
                    master[row+i] = false;
                    slave[row-i+n-1] = false;
 
                }
            }
        }
    }

    public List<String> convert(Stack<Integer> stack, int n) {
        List<String> result = new ArrayList();
        for(int i = 0; i < n; i++) {
            int k = stack.get(i);
            StringBuilder sb = new StringBuilder();
            for(int j = 0; j < n; j++) {
                if(j == k) {
                    sb.append('Q');
                } else {
                    sb.append('.');
                }
            }
            result.add(sb.toString());
        }
        return result;
    }
}

26. leetcode 22.括號生成

class Solution {
    public List<String> generateParenthesis(int n) {
        if(n == 0) return result;
        Stack<Character> stack = new Stack();
        backtrack(n, 0, 0, stack);
        return result;
    }

    public List<String> result = new ArrayList();
    public void backtrack(int n, int left, int right, Stack<Character> stack) {
        if(left == right && right == n) {
            StringBuilder sb = new StringBuilder();
            for(int i = 0; i < stack.size(); i++) {
                sb.append(stack.get(i));
            }
            result.add(sb.toString());
        } else {
            if(left == right && left < n) {
                stack.add('(');
                backtrack(n, left+1, right, stack);
                stack.pop();
            } else if (left > right && left < n) {
                stack.add('(');
                backtrack(n, left+1, right, stack);
                stack.pop();
                stack.add(')');
                backtrack(n, left, right+1, stack);
                stack.pop();
            } else if(left > right && left == n) {
                stack.add(')');
                backtrack(n, left, right+1, stack);
                stack.pop();
            }
        }
    }
}

解法二(動態規划)

class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> result = new ArrayList();
        List<List<String>> dp = new ArrayList();
        dp.add(Arrays.asList(""));
        dp.add(Arrays.asList("()"));
        for(int i = 2; i <= n; i++) {
            List<String> tmp = new ArrayList();
            for(int j = 0; j < i; j++) {
                List<String> p = dp.get(j);
                List<String> q = dp.get(i - 1 - j);
                for(String t1 : p) {
                    for(String t2 : q) {
                        String tt = "(" + t1 + ")" + t2;
                        tmp.add(tt);
                    }
                }
            }
            dp.add(tmp);
        }
        return dp.get(n);
    }
}

27, leetcode 39. 組合總和

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        backtrack(candidates, target, new Stack<Integer>(), 0, 0);
        return result;
    }
    public List<List<Integer>> result = new ArrayList();

    public void backtrack(int[] candidates, int target, Stack<Integer> stack, int sum, int start) {
        if(sum == target) {
            List<Integer> tmp = new ArrayList();
            for(int i = 0; i < stack.size(); i++) {
                tmp.add(stack.get(i));
            }
            result.add(tmp);
        } else {
            if(sum < target) {
                for(int i = start; i < candidates.length; i++) {
                    if(sum + candidates[i] <= target) {
                        stack.add(candidates[i]);
                        backtrack(candidates, target, stack, sum+candidates[i], i);
                        stack.pop();
                    }
                }
            }
        }
    }
}

28, leetcode 95. 不同的二叉搜索樹II

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<TreeNode> generateTrees(int n) {
        if(n == 0) return res;
        return backtrack(1, n);
    }
    List<TreeNode> res = new ArrayList();
    public List<TreeNode> backtrack(int start, int end) {
        List<TreeNode> result = new ArrayList();
        if(start > end) {
            result.add(null);
            return result;
        }
        if(start == end) {
            TreeNode node = new TreeNode(start);
            result.add(node);
            return result;
        }
        for(int i = start; i <= end; i++) {
            List<TreeNode> leftNode = backtrack(start, i-1);
            List<TreeNode> rightNode = backtrack(i+1, end);
            for(TreeNode left : leftNode) {
                for(TreeNode right : rightNode) {
                    TreeNode node = new TreeNode(i);
                    node.left = left;
                    node.right = right;
                    result.add(node);
                }
            }
        }
        return result;
    }
}

動態規划非常好的題解

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<TreeNode> generateTrees(int n) {
        List<List<TreeNode>> dp = new ArrayList();
        List<TreeNode> tt = new ArrayList();
        tt.add(null);
        dp.add(tt);
        if(n == 0) return new ArrayList();
        for(int i = 1; i <= n; i++) {
            List<TreeNode> tmp = new ArrayList();
            for(int j = 1; j <= i; j++) {
                List<TreeNode> left = dp.get(j-1);
                List<TreeNode> right = dp.get(i-j);
                for(TreeNode l : left) {
                    for(TreeNode r : right) {
                        TreeNode node = new TreeNode(j);
                        node.left = l;
                        node.right = clone(r, j);
                        tmp.add(node);
                    }
                }
            }
            dp.add(tmp);
        }
        return dp.get(n);
    }
    public TreeNode clone(TreeNode node, int offset) {
        if(node == null) {
            return null;
        }
        TreeNode n = new TreeNode(node.val+offset);
        n.left = clone(node.left, offset);
        n.right = clone(node.right, offset);
        return n;
    }
}

29, leetcode 98.驗證二叉搜索樹

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isValidBST(TreeNode root) {
        return isBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
    }

    public boolean isBST(TreeNode root, long minVal, long maxVal) {
        if(root == null) {
            return true;
        }
        if(root.val <= minVal || root.val >= maxVal) {
            return false;
        }
        return isBST(root.left, minVal, root.val)&&isBST(root.right, root.val, maxVal);
    }
}

這里需要注意最大值最小值的類型,需要用Long 或者Double來表示,Integer不行。比如[Integer.MAX_VALUE]這個例子就會報錯。 這個解法不通用,雖然簡單。下面寫一個更常用的解法,采用中序遍歷來判斷BST

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isValidBST(TreeNode root) {
        return isBST(root);
    }
    public TreeNode pre = null;
    public boolean isBST(TreeNode root) {
       if(root == null) return true;
       if(!isBST(root.left)) {
           return false;
       }
       if(pre != null && pre.val >= root.val) {
           return false;
       }
       pre = root;
       return isBST(root.right);
    }
}

30, leetcode 104.二叉樹的最大深度

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null) return depth;
        queue.add(root);
        while(!queue.isEmpty()) {
            int len = queue.size();
            for(int i = 0; i < len ; i++) {
                TreeNode tmp = queue.remove();
                if(tmp.left != null) queue.offer(tmp.left);
                if(tmp.right != null) queue.offer(tmp.right);
            }
            depth++;
        }
        return depth;
    }
    public int depth = 0;
    public Queue<TreeNode> queue = new LinkedList();   
}

方法二(遞歸):

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        int maxL = maxDepth(root.left);
        int maxR = maxDepth(root.right);
        return Math.max(maxL, maxR)+1;
    }
}

31 leetcode 99. 恢復二叉樹

難點:

很難想到恢復的細節,主要是找到規律

記錄兩個節點,第一個節點是第一次前面節點大於后面節點,取前節點

第二個節點是第二次出現的前面節點大於后面節點,取后面節點。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public void recoverTree(TreeNode root) {
        inOrder(root);
        int tmp = first.val;
        first.val = second.val;
        second.val = tmp;
    }
    public TreeNode first = null;
    public TreeNode pre = new TreeNode(Integer.MIN_VALUE);
    public TreeNode second = null;
    public void inOrder(TreeNode root) {
        if(root == null) return;
        inOrder(root.left);
        if (first == null && pre != null && pre.val > root.val){
            first = pre;
        }
        if(first != null && pre != null && pre.val > root.val) {
            // int tmp = root.val;
            // root.val = first.val;
            // first.val = tmp;
            // return;
            second = root;
        }
        pre = root;
        inOrder(root.right);
    }
}

32. leetcode 105. 從前序與中序遍歷序列構造二叉樹

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        Map<Integer, Integer> map = new HashMap<>();
        for(int i = 0; i < inorder.length; i++) {
            map.put(inorder[i], i);
        }
        return buildTreeHelper(preorder, 0, preorder.length, inorder, 0, inorder.length, map);
    }

    public TreeNode buildTreeHelper(int[] preorder, int p_start, int p_end, int[] inorder, int i_start, int i_end, Map<Integer, Integer>map){
        if(p_start == p_end) {
            return null;
        }
        int root_val = preorder[p_start];
        TreeNode root = new TreeNode(root_val);
        int i_root = map.get(root_val);
        int len = i_root - i_start;
        root.left = buildTreeHelper(preorder, p_start+1, p_start+len+1, inorder, i_start, i_root, map);
        root.right = buildTreeHelper(preorder, p_start+len+1, p_end, inorder, i_root+1, i_end, map);
        return root;
    }

}

33. leetcode 106. 從中序與后序遍歷序列構造二叉樹

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        Map<Integer, Integer> map = new HashMap<>();
        for(int i = 0; i < inorder.length; i++) {
            map.put(inorder[i], i);
        }
        return buildTreeHelper(inorder, 0, inorder.length, postorder, 0, postorder.length, map);
    }

    public TreeNode buildTreeHelper(int[] inorder, int i_start, int i_end, int[] postorder, int p_start, int p_end, Map<Integer, Integer> map) {
        if(i_start == i_end) {
            return null;
        }
        int root_val = postorder[p_end-1];
        TreeNode root = new TreeNode(root_val);
        int index = map.get(root_val);
        int len = index - i_start;
        root.left = buildTreeHelper(inorder, i_start, index, postorder, p_start, p_start+len, map);
        root.right = buildTreeHelper(inorder, index+1, i_end, postorder, p_start+len, p_end-1, map);
        return root;
    }
}

34. leetcode 107. 二叉樹的層次遍歷II

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        List<List<Integer>> result = new ArrayList<>();
        if(root == null) return result;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()) {
            List<Integer> tmp = new ArrayList<>();
            int len = queue.size();
            for(int i = 0; i < len; i++) {
                TreeNode cur = queue.remove();
                if(cur.left != null) queue.offer(cur.left);
                if(cur.right != null) queue.offer(cur.right);
                tmp.add(cur.val);
            }
            result.add(tmp);
        }
        Collections.reverse(result);
        return result;
    }
}

35. leetcode 24. 兩兩交換鏈表中的節點

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode tmp = null;
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode pre = head;
        ListNode ppre = dummy;
        while(pre != null) {
            if(pre.next != null) {
                tmp = pre.next.next;
                ppre.next = pre.next;
                pre.next.next = pre;
                pre.next = tmp;
                ppre = pre;
            }
            pre = pre.next;
        }
        return dummy.next;
    }
}

36. leetcode 25. K個一組翻轉鏈表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode pre = dummy;
        ListNode end = dummy;

        while(end.next != null) {
            for(int i = 0; i < k && end != null; i++) end = end.next;
            if(end == null) break;
            ListNode start = pre.next;
            ListNode next = end.next;
            end.next = null;
            pre.next = reverse(start);
            start.next = next;
            pre = start;
            end = start;
        }
        return dummy.next;
    }

    public ListNode reverse(ListNode head) {
        ListNode pre = null;
        ListNode node = head;
        while(node != null) {
            ListNode tmp = node.next;
            node.next = pre;
            pre = node;
            node = tmp;
        }
        return pre;
    }
}

這題寫的有點難受,主要是小bug太多,邏輯也不夠清晰。這道題是自己的弱項,需要多練習。

37. leetcode 61.旋轉鏈表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        if(k == 0) return head;
        if(head == null) return head;
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode pre = dummy;
        ListNode end = dummy;
        dummy.next = findTailK(head, k, head);
        return dummy.next;

    }

    public ListNode findTailK(ListNode head, int k, ListNode start) {
        ListNode cur = head;
        ListNode next = head;
        int count = 0;
        for(int i = 0; i < k && next != null; i++) {
            next = next.next;
            count++;
        }
        if(next == null) return findTailK(head, k%count, start);
        while(next.next != null) {
            next = next.next;
            cur = cur.next;
        }
        next.next = start;
        ListNode result = cur.next;
        cur.next = null;
        return result;
    }
}

38. leetcode 29.兩數相除

class Solution {
    public int divide(int dividend, int divisor) {
        int count = 0; 
        if(dividend == Integer.MIN_VALUE && divisor == -1) return Integer.MAX_VALUE;
        if(divisor == 0) return 0;
        if(divisor == 1) return dividend; 
        boolean sign = (dividend > 0) ^ (divisor > 0);
        long s = Math.abs((long)divisor);
        long d = Math.abs((long)dividend);
       
        int result = 0;
        for(int i = 31; i >= 0; i--) {
            long tmp = d >> i;
            result <<= 1;
            if(tmp >= s) {
                result += 1;
                d -= s << i;
            }
        }
        return sign ? -result : result;
    }
}

這道題邊界情況很煩。一方面要考慮被除數負數溢出(絕對值時候用到),一方面又要考慮結果溢出。debug了好久。

39. leetcode 43.字符串相乘

class Solution {
    public String multiply(String num1, String num2) {
        int len1 = num1.length();
        int len2 = num2.length();
        if(num1.equals("0") || num2.equals("0")) return "0";
        int[] result = new int[len1+len2];
        for(int i = len1-1; i >= 0; i--) {
            for(int j = len2-1; j >= 0; j--) {
                int tmp = (result[i+j+1]) + (num1.charAt(i) - '0')*(num2.charAt(j) - '0');
                result[i+j+1] = tmp%10;
                result[i+j] += tmp/10;
            }
        }
        StringBuilder res = new StringBuilder();
        for(int i = 0; i < result.length; i++) {
            if(result[i] != 0) {
                for(int j = i; j < result.length; j++) {
                    res.append(result[j]);
                }
                break;
            }
        }
        return res.toString();
    }
}

40. leetcode 50. Pow(x, n)

class Solution {
    public double myPow(double x, int n) {
        if(n == 0) return 1.0;
        if(n > 0) return powHelper(x, n);
        if(n < 0) {
            if(n == Integer.MIN_VALUE && x != 1.0 && x != -1.0) {
                return 0.0;
            }
            return 1/powHelper(x, -n);
        };
        return 1.0;
    }
    public double powHelper(double x, int n) {
        double res = 1;
        while(n > 0) {
            if((n&1) == 1) res *= x;
            n >>= 1;
            x *= x;
        }
        return res;
    }
}

還是邊界情況很煩人。不過這題的思路很通用,利用二進制的拆解思路來做的,很棒了。

還有一種辦法是二分法。后面完善。

41. leetcode 60. 第k個排列

class Solution {
    public String getPermutation(int n, int k) {
        boolean[] visited = new boolean[n];
        int[] nums = new int[n];
        for(int i = 0; i < n; i++) {
            nums[i] = i+1;
        }
        StringBuilder sb = new StringBuilder();
        List<String> res = new ArrayList<>();
        permutationCore(visited, nums, k, sb, n, res);
        result = res.get(k-1);
        return result;
    }
    public String result = "";

    public void permutationCore(boolean[] visited, int[] nums, int k, StringBuilder str, int n, List<String> res) {
        if(str.length() == n) {
            res.add(str.toString());
            return;
        } else if(k < 0) {
            return;
        } else {
            for(int i = 0; i < n; i++) {
                if(!visited[i]) {
                    visited[i] = true;
                    permutationCore(visited, nums, k, str.append(nums[i]), n, res);
                    str.deleteCharAt(str.length()-1);
                    visited[i] = false;
                }
            }
        }
    }
}

執行用時 :909 ms, 在所有 java 提交中擊敗了5.44%的用戶
內存消耗 :103.2 MB, 在所有 java 提交中擊敗了11.35%的用戶

42. leetcode 67.二進制求和

class Solution {
    public String addBinary(String a, String b) {
        int len1 = a.length();
        int len2 = b.length();
        if(len1 < len2) return addBinary(b, a);
        int[] result = new int[len1+1];

        int index = len1 - 1;
        int j = len2 - 1;
        while(index >= 0) {
            int tmp = j >= 0 ? (a.charAt(index) - '0') + (b.charAt(j) - '0') + result[index+1] : (a.charAt(index) - '0') + result[index+1];
            result[index+1] = tmp%2;
            result[index] += tmp/2;
            index--;
            j--;
        }
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < result.length; i++) {
            if(i == 0 && result[i] == 0) continue;
            sb.append(result[i]);
        }
        return sb.toString();
    }
}

43. leetcode 69. x的平方根

class Solution {
    public int mySqrt(int x) {
        if(x == 1) return 1;
        for(int i = 1; i <= x/2; i++) {
            if(i*i <= x && (long)(i+1)*(i+1) > (long)x) {
                return i;
            }
        }
        return 0;
    }
}

解法二(二分查找):

class Solution {
    public int mySqrt(int x) {
       long left = 0;
       long right = x / 2 + 1;
       while(left < right) {
           long mid = (left + right+1) >> 1;
           if((long)mid*mid > (long)x) {
               right = mid-1;
           } else {
               left = mid;
           }
       }
       return (int)left;
    }
}

44. leetcode 168. Excel表列名稱

class Solution {
    public String convertToTitle(int n) {
        StringBuilder sb = new StringBuilder();
        convertHelper(n, sb);
        return sb.reverse().toString();
    }

    public void convertHelper(int n, StringBuilder res) {
        if(n == 0) {
            return;
        } else {
            res.append((char)('A' + (n-1)%26));
            convertHelper((n-1)/26, res);
        }
    }
}

45. leetcode 66. 加一

class Solution {
    public int[] plusOne(int[] digits) {
        int len = digits.length;
        int carry = 0;
        int[] result = new int[len+1];
        for(int i = len-1; i >= 0; i--) {
            if(i == len - 1) {
                result[i+1] = (digits[i] + carry + 1) % 10;
                carry = (digits[i] + carry + 1) / 10;
            } else {
                result[i+1] = (digits[i] + carry) % 10;
                carry = (digits[i] + carry) / 10;
            }
        }
        if(carry == 1) {
            result[0] = 1;
            return result;
        } else {
            int[] res = new int[len];
            for(int i = 0; i < len; i++) {
                res[i] = result[i+1];
            }
            return res;
        }
    }
}

46. leetcode 77.組合

class Solution {
    public List<List<Integer>> combine(int n, int k) {
        Stack<Integer> stack = new Stack();
        backtrack(0, n, k, stack, 0);
        return result;
    }
    public List<List<Integer>> result = new ArrayList<>();
    public void backtrack(int start, int n, int k, Stack<Integer> stack, int depth) {
        if(depth == k) {
            List<Integer> tmp = new ArrayList();
            for(int i = 0; i < stack.size(); i++) {
                tmp.add(stack.get(i));
            }
            result.add(tmp);
        } else {
            for(int i = start; i < n; i++) {
                stack.add(i+1);
                backtrack(i+1, n, k, stack, depth+1);
                stack.pop();
            }
        }
    }
}

47. leetcode 70.爬樓梯

動態規划特訓

class Solution {
    public int climbStairs(int n) {
        int[] dp = new int[n];
        dp[0] = 1;
        if(n == 1) return dp[0];
        dp[1] = 2;
        for(int i = 2; i < n; i++) {
            dp[i] = dp[i-1] + dp[i-2];
        }
        return dp[n-1];
    }
}

動態規划四步法:

  • 問題拆解(找到問題之間的具體關系)
  • 狀態定義(用自己的話定義狀態,很重要是dp[i] 還是dp_[ i ]__[ j ]_ )
  • 遞推方程推導(選擇合適的數據結構表示對應的狀態)
  • 實現(注意如何初始化,數組需要申請多大的空間)

方法二:(依然是動態規划,不過更節省了空間)

class Solution {
    public int climbStairs(int n) {
        int a = 1;
        int b = 2;
        int c = 0;
        if(n == 1) return a;
        if(n == 2) return b;
        for(int i = 3; i <= n; i++) {
            c = a + b;
            a = b;
            b = c;
        }
        return c;
    }
}

有了四步解題法模板,再也不害怕動態規划!

(進階版)有了四步解題法模板,再也不害怕動態規划!

動態規划總結與題目分類

48. leetcode 120.三角形最小路徑和

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        int row = triangle.size();
        int col = triangle.get(row-1).size();
        int[][] dp = new int[row][col];
        for(int i = 0; i < col; i++) {
            dp[row-1][i] = triangle.get(row-1).get(i);
        }

        for(int i = col-2; i >= 0; i--) {
            List<Integer> tmp = triangle.get(i);
            for(int j = 0; j < tmp.size(); j++) {
                dp[i][j] = Math.min(dp[i+1][j], dp[i+1][j+1]) + triangle.get(i).get(j);
            }
        }
        return dp[0][0];
    }
}

解法二(動態規划,但是節省動態規划申請的dp空間)

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        int row = triangle.size();
        int col = triangle.get(row-1).size();
        int[] dp = new int[col];
        for(int i = 0; i < col; i++) {
            dp[i] = triangle.get(row-1).get(i);
        }

        for(int i = col-2; i >= 0; i--) {
            List<Integer> tmp = triangle.get(i);
            for(int j = 0; j < tmp.size(); j++) {
                dp[j] = Math.min(dp[j], dp[j+1]) + triangle.get(i).get(j);
            }
        }
        return dp[0];
    }
}

將二維數組變為一維數組,原因是,數組狀態的更新不影響后續的更新操作

49. leetcode 53. 最大子序和

class Solution {
    public int maxSubArray(int[] nums) {
        //記憶化搜索
        int len = nums.length;
        int[] dp = new int[len+1];
        dp[0] = 0;
        int maxVal = Integer.MIN_VALUE;
        for(int i = 0; i < len; i++) {
            int tmp = dp[i] > 0 ? dp[i] + nums[i] : nums[i];
            dp[i+1] = tmp;
            if(dp[i+1] > maxVal) maxVal = dp[i+1];
        }
        return maxVal;  
    }
}

50. leetcode 62. 不同路徑

解法一:(常規動態規划)

class Solution {
    public int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];
        for(int i = 0; i < m; i++) {
            dp[i][0] = 1;
        }
        for(int i = 0; i < n; i++) {
            dp[0][i] = 1;
        }
        for(int i = 1; i < m; i++) {
            for(int j = 1; j < n; j++) {
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }
        }
        return dp[m-1][n-1];
    }
}

解法二:(節省內存/線性動態規划)


這么做的原因還是因為前面狀態的變化不影響后面狀態的更新,所以可以壓縮為線性的空間。

51. leetcode 63. 不同路徑II

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        int[][] dp = new int[m][n];
        boolean flag = true;
        for(int i = 0; i < n; i++) {
            if(obstacleGrid[0][i] == 1) flag = false;
            if(flag) {
                dp[0][i] = 1;
            }
        }
        flag = true;
        for(int i = 0; i < m; i++) {
            if(obstacleGrid[i][0] == 1) flag = false;
            if(flag) {
                dp[i][0] = 1;
            }
        }
        for(int i = 1; i < m; i++) {
            for(int j = 1; j < n; j++) {
                dp[i][j] = obstacleGrid[i][j] == 1 ? 0 : dp[i-1][j]+dp[i][j-1];
            }
        }
        return dp[m-1][n-1];
    }
}

52. leetcode 64. 最小路徑和

class Solution {
    public int minPathSum(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        int[][] dp = new int[m+1][n+1];
        for(int i = 0; i < m+1; i++) {
            dp[i][0] = Integer.MAX_VALUE;
        }
        for(int i = 0; i < n+1; i++) {
            dp[0][i] = Integer.MAX_VALUE;
        }
        dp[1][1] = grid[0][0];
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                if(i == 0 && j == 0) continue;
                dp[i+1][j+1] = Math.min(dp[i][j+1], dp[i+1][j]) + grid[i][j];
            }
        }
        return dp[m][n];
    }
}

53. leetcode 221. 最大正方形

class Solution {
    public int maximalSquare(char[][] matrix) {
        int m = matrix.length;
        if(m == 0) return 0;
        int n = matrix[0].length;
        int[][] dp = new int[m][n];
        int maxVal = dp[0][0];
        for(int i = 0; i < m; i++) {
            dp[i][0] = matrix[i][0] == '1' ? 1 : 0;
            if(dp[i][0] == 1) maxVal = 1;
        }
        for(int i = 0; i < n; i++) {
            dp[0][i] = matrix[0][i] == '1' ? 1 : 0;
            if(dp[0][i] == 1) maxVal = 1;
        }
        for(int i = 1; i < m; i++) {
            for(int j = 1; j < n; j++){
                if(matrix[i][j] == '1') {
                    if(isSquare(matrix, dp[i-1][j-1], i, j)) {
                        dp[i][j] = dp[i-1][j-1] + 1 + 2*(int)Math.sqrt(dp[i-1][j-1]);
                        if(dp[i][j] > maxVal) {
                            maxVal = dp[i][j];
                        }
                    }
                } else {
                    dp[i][j] = 0;
                }
            }
        }
        return maxVal;
    }

    boolean isSquare(char[][] matrix, int tmp, int m, int n) {
        int len = (int)Math.sqrt(tmp);
        for(int i = m - len; i < m; i++) {
            if(matrix[i][n] != '1') {
                return false;
            }
        }
        for(int j = n - len; j < n; j++) {
            if(matrix[m][j] != '1') {
                return false;
            }       
        }
        return true;
    }
}

還是有幾個例子沒有通過。

class Solution {
    public int maximalSquare(char[][] matrix) {
        int m = matrix.length;
        if(m == 0) return 0;
        int n = matrix[0].length;
        int[][] dp = new int[m][n];
        int maxVal = 0;
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
               if(matrix[i][j] == '1') {
                    if(i == 0 || j == 0) {
                        dp[i][j] = matrix[i][j] - '0';
                    } else {
                        dp[i][j] = Math.min(dp[i-1][j], Math.min(dp[i][j-1], dp[i-1][j-1])) + 1;
                    }
                    maxVal = Math.max(dp[i][j], maxVal);
               }
            }
        }
        return maxVal*maxVal;
    }

    
}

54. leetcode 887.雞蛋掉落

解法一:(套用動態規划模板,超時)

class Solution {
    public int superEggDrop(int K, int N) {
        int[][] dp = new int[K+1][N+1];
        
        return eggHelper(K, N, dp);
    }

    public int eggHelper(int K, int N, int[][] dp) {
        if(K == 1) return N;
        if(N == 0) return 0;
        if(dp[K][N] != 0) return dp[K][N];
        int res = N;
        for(int i = 1; i <= N; i++) {
            res = Math.min(res, Math.max(eggHelper(K-1, i-1, dp), eggHelper(K, N-i, dp)) + 1);
        }
        dp[K][N] = res;
        return res;
    }
}

題目要求是求最壞情況下,最小的移動次數:

  • 狀態定義f(K, N) (不是數組,而是一個函數,其實是遞歸),為K個雞蛋,N層樓最壞情況下,最小移動次數。

  • 狀態轉移方程 :

  • for i in range(1, N+1): (遍歷N層樓,即遍歷每一次選擇, 選擇的改變導致狀態的改變)
    	res = min(res, max(f(K-1, i-1), f(K, N-i)) + 1)    
            # max(f(K-1, i-1), f(K, N-i)) + 1 保證該選擇下最壞的情況
    
  • 超時的原因主要是: 下面這段代碼是線性地搜索最小值,每一次只會變化一個狀態。下面二分搜索的優化思路就是針對這一點進行優化。

    • res = Math.min(res, Math.max(eggHelper(K-1, i-1, dp), eggHelper(K, N-i, dp)) + 1);
  • 時間復雜度: 子問題個數* 函數本身復雜度

    • 函數本身復雜度,里面只有一個for循環,所以為O(N)
    • 子問題個數:不同狀態組合的總數,f(k,n) 的個數,O(k*n)
    • 所以時間復雜度為O(k*N^2)
  • 空間復雜度:dp申請的空間/子問題個數 = k*n

解法二:(動態規划+二分查找)

class Solution {
    public int superEggDrop(int K, int N) {
        int[][] dp = new int[K+1][N+1];
        
        return eggHelper(K, N, dp);
    }

    public int eggHelper(int K, int N, int[][] dp) {
        if(K == 1) return N;
        if(N == 0) return 0;
        if(dp[K][N] != 0) return dp[K][N];
        int res = N;
        // for(int i = 1; i <= N; i++) {
        //     res = Math.min(res, Math.max(eggHelper(K-1, i-1, dp), eggHelper(K, N-i, dp)) + 1);
        // }
        int lo = 1, hi = N;
        while(lo <= hi) {
            int mid = (lo + hi) >> 1;
            int broken = eggHelper(K-1, mid-1, dp);
            int not_broken = eggHelper(K, N - mid, dp);
            if(broken > not_broken) {
                hi = mid - 1;
                res = Math.min(res, broken+1);
            } else {
                lo = mid+1;
                res = Math.min(res, not_broken + 1);
            }
        }
        dp[K][N] = res;
        return res;
    }
}
  • 可以使用二分查找的原因是,能夠發現規律,當eggHelper(K-1, i-1, dp) == eggHelper(K, N-i, dp)) 此時res取值最小。
  • 二分查找時間復雜度為O(log(N)),所以整個函數的時間復雜度為O(kNlog(N))

解法三(動態規划/ 更換狀態的定義/ 特殊的狀態定義)

class Solution {
    public int superEggDrop(int K, int N) {
        int[][] dp = new int[K+1][N+1];
        int m = 0;
        while(dp[K][m] < N) {
            m++;
            for(int i = 1; i <= K; i++) {
                dp[i][m] = dp[i-1][m-1] + dp[i][m-1] + 1;
            }
        }
        return m;
    }
}

55. leetcode 300. 最長上升子序列

class Solution {
    public int lengthOfLIS(int[] nums) {
        int len = nums.length;
        if(len == 0) return 0;
        int[] dp = new int[len];
        for(int i = 0; i < len; i++) {
            dp[i] = 1;
        }
        int maxVal = 1;
        for(int i = 1; i < len; i++) {
            for(int j = 0; j < i; j++) {
                if(nums[j] < nums[i]) {
                    dp[i] = Math.max(dp[i], dp[j]+1);
                }
            }
            if(dp[i] > maxVal) {
                maxVal = dp[i];
            }
        }
        return maxVal;
    }
}

56. leetcode 198.打家劫舍

class Solution {
    public int rob(int[] nums) {
        int len = nums.length;
        if(len == 0) return 0;
        int[] dp = new int[len];
        if(len >= 1) dp[0] = nums[0];
        if(len >= 2) dp[1] = Math.max(nums[0], nums[1]);
        for(int i = 2; i < len; i++) {
            dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i]);
        }
        return dp[len-1];
    }
}

todo:

303 √413 √343 √279 √646 √

376 √416 √494 474 322

518 139 377 309 714

123 188 583 72 650

正則表達式

刷題心得:

一直都在用中文版力扣刷題,可是很多題解寫的不是很詳細,或者有些看起來很吃力,這時候可以切換到美國版的leetcode到題解區去找找靈感,以下是切換方法。

中文版題目url:https://leetcode-cn.com/problems/longest-common-subsequence/

美國版url: https://leetcode.com/problems/longest-common-subsequence/

發現了沒有,把中文版里面的'-cn'刪掉 然后回車就可以訪問了。

57. leetcode 303. 區域和檢索-數組不可變

class NumArray {

    public NumArray(int[] nums) {
        int len = nums.length;
        if(len != 0) {
            dp = new int[len];
            dp[0] = nums[0];
            for(int i = 1; i < len; i++) {
                dp[i] = dp[i-1] + nums[i];
            }
        } 
    }
    public int[] dp;
    
    public int sumRange(int i, int j) {
        if(dp.length == 0) return 0;
        if(i == 0) return dp[j];
        return dp[j]-dp[i-1];
    }
}

/**
 * Your NumArray object will be instantiated and called as such:
 * NumArray obj = new NumArray(nums);
 * int param_1 = obj.sumRange(i,j);
 */

這里依然使用動態規划的思想,將反復利用的數值保存起來。不過需要注意臨界條件。

58. leetcode 413. 等差數列划分

解法一:(奇淫技巧)

class Solution {
    public int numberOfArithmeticSlices(int[] A) {
        int sum = 0;
        int cur = 0;
        int len = A.length;
        if(len <= 2) return 0;
        for(int i = 2; i < len; i++) {
            if(A[i]- A[i-1] == A[i-1] - A[i-2]) {
                cur += 1;
                sum += cur;
            } else {
                cur = 0;
            }
        }
        return sum;
    }
}

這里的 sum += cur;

cur 每次加一 表示多一個新的元素,新創造cur個組合。\

[1, 2, 3, 4]_[ 1, 2,3] 時,cur = 1, 當4加入時,cur變為2,表示新增兩個組合分別為[2, 3, 4][1, 2, 3, 4]

但是這種解法不能當作模板來解其他類型的題目。不能學

解法二:(動態規划)

class Solution {
    public int numberOfArithmeticSlices(int[] A) {
        int len = A.length;
        if(len <= 2) return 0;
        int[] dp = new int[len];
        int result = 0;
        for(int i = 2; i < len; i++) {
            if(A[i] - A[i-1] == A[i-1] - A[i-2]) {
                dp[i] = dp[i-1] + 1;
                result += dp[i];
            }
        }
        return result;
    }
}

其實比較難的還是:

  • 狀態定義要准確: 以i為結尾的等差數列的個數
  • result += dp[i] 這個要能想到

59. leetcode 343. 整數拆分

class Solution {
    public int integerBreak(int n) {
        int[] dp = new int[n+1];
        if(n == 2) return 1;
        if(n == 3) return 2;
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 3;
        int maxVal = 0;
        for(int i = 4; i <= n; i++) {
            for(int j = 1; j <= i/2; j++) {
                dp[i] = Math.max(dp[i], dp[j]*dp[i-j]);
            }
        }
        return dp[n];
    }
}

60. leetcode 279. 完全平方數

解法一(超時)

class Solution {
    public int numSquares(int n) {
        if(n == 0) return 0;
        int[] dp  = new int[n+1];
        dp[0] = 0;
        for(int i = 1; i <= n; i++) {
            dp[i] = Integer.MAX_VALUE;
        }
        for(int i = 1; i <= n; i++) {
            for(int j = 0; j < i; j++) {
                if(isSqrt((double)(i - j))) {
                    dp[i] = Math.min(dp[i], dp[j] + 1);
                }
            }
        }
        return dp[n];
    }

    public boolean isSqrt(double num) {
        double tmp = Math.sqrt(num);
        if(tmp - (int) tmp != 0) {
            return false;
        }
        return true;
    }
}

運行超時,主要是有兩層循環,導致時間復雜度為O(n^2)

解法二(小處理技巧)

class Solution {
    public int numSquares(int n) {
        if(n == 0) return 0;
        int[] dp  = new int[n+1];
        dp[0] = 0;
        for(int i = 1; i <= n; i++) {
            dp[i] = Integer.MAX_VALUE;
        }
        for(int i = 1; i <= n; i++) {
            for(int j = 1; i - j*j >= 0; j++) {
                dp[i] = Math.min(dp[i], dp[i -j*j] + 1);
            }
        }
        return dp[n];
    }
}

兩個處理方式有些不同,第一種通過判斷i-j 是不是平方數

第二種方法,通過i-j*j i直接減去平方數,然后從數組里面找,這樣很節省時間。別樣的剪枝手段。

61. leetcode 646. 最長對數鏈

class Solution {
    public int findLongestChain(int[][] pairs) {
        int len = pairs.length;
        if(pairs == null || len == 0) return 0;
        Arrays.sort(pairs, (a, b)->(a[0] - b[0])); //good
        int[] dp = new int[len];
        Arrays.fill(dp, 1);//good
        for(int i = 0; i < len; i++) {
            for(int j = 0; j < i; j++) {
                if(pairs[i][0] > pairs[j][1]) {
                    dp[i] = Math.max(dp[i], dp[j]+1);
                }
            }
        }
        return dp[len-1];
    }
}

這里對二維數組進行排序的操作非常值得學習。

62. leetcode 376. 擺動序列

解法一:

class Solution {
    public int wiggleMaxLength(int[] nums) {
        int len = nums.length;
        if(len == 0) return 0;
        int[][] dp = new int[len][2];
        for(int i = 0; i < len; i++) {
            dp[i][0] = 1;
        }
        int maxLen = 1;
        for(int i = 1; i < len; i++) {
            for(int j = 0; j < i; j++) {
                if(j == 0 && nums[i] != nums[j]) {
                    dp[i][0] = Math.max(dp[i][0], dp[j][0]+1);
                    dp[i][1] = nums[j] > nums[i] ? -1 : 1;
                } else if((nums[j] > nums[i] && dp[j][1] > 0) ||( nums[j] < nums[i] && dp[j][1] < 0)) {
                    dp[i][0] = Math.max(dp[i][0], dp[j][0] + 1);
                    dp[i][1] = nums[j] > nums[i] ? -1 : 1;
                }
            }
            maxLen = Math.max(maxLen, dp[i][0]);
        }
        return maxLen;
    }
}

運行耗時很長:10ms,擊敗8.43%

解法二:

class Solution {
    public int wiggleMaxLength(int[] nums) {
        int len = nums.length;
        if(len == 0) return 0;
        int[] up = new int[len];
        int[] down = new int[len];
        up[0] = 1;
        down[0] = 1;
        for(int i = 1; i < len; i++) {
            if(nums[i] > nums[i-1]) {
                up[i] = down[i-1] + 1;
                down[i] = down[i-1];
            } else if(nums[i] < nums[i-1]) {
                down[i] = up[i-1] + 1;
                up[i] = up[i-1];
            } else {
                up[i] = up[i-1];
                down[i] = down[i-1];
            }
        }
        return Math.max(up[len-1], down[len-1]);
        
    }
}

比較新穎的動態規划處理手段。值得學習。

63. leetcode 416. 分割等和子集

class Solution {
    public boolean canPartition(int[] nums) {
        int len = nums.length;
        int sum = 0;
        for(int num : nums) {
            sum += num;
        }
        if(sum%2 != 0) return false;
        int target = sum / 2;
        boolean[] dp = new boolean[target+1];

        for(int i = 0; i <= target; i++) {
            if(nums[0] == i) dp[i] = true;
        }

        for(int i = 1; i < len; i++) {
            for(int j = target; j >= nums[i]; j--) {
                dp[j] = dp[j] || dp[j - nums[i]];
            }
        }
        return dp[target];
    }
}

64. leetcode 10. 正則表達式匹配

class Solution {
    public boolean isMatch(String s, String p) {
        int lens = s.length();
        int lenp = p.length();
        // if(lenp == 0) return false;
        dp = new int[lens+1][lenp+1];
        return isMatchCore(0, 0, s, p);

    }
    public int[][] dp;

    public boolean isMatchCore(int i, int j, String s, String p) {
        if(j == p.length()) return i == s.length();
        if(i <= s.length() && j <= p.length() && dp[i][j] != 0) return dp[i][j] > 0;

        boolean firstMatch = i < s.length() && (p.charAt(j) == s.charAt(i) || p.charAt(j) == '.');
        boolean ans = false;
        if(j + 1 < p.length() && p.charAt(j+1) == '*') {
            ans = isMatchCore(i, j+2, s, p) || firstMatch && isMatchCore(i+1, j, s, p);
        } else {
            ans = firstMatch && isMatchCore(i+1, j+1, s, p);
        }
        if(i <= s.length() && j <= p.length()) dp[i][j] = ans ? 1 : -1;
        return ans;
    }
}

遞歸+備忘錄->動態規划

值得多刷幾遍,很多細節需要處理。

65. leetcode 494. 目標和

解法一:(回溯,超時)

class Solution {
    public int findTargetSumWays(int[] nums, int S) {
        if(nums.length == 0) return 0;
        Stack<Integer> stack = new Stack<>();
        backtrack(nums, S, stack);
        return ans;
    }
    public int ans = 0;

    public void backtrack(int[] nums, int S, Stack<Integer> stack) {
        if(stack.size() == nums.length) {
            int result = 0;
            for(int i = 0; i < stack.size(); i++) {
                if(stack.get(i) == 1) {
                    result += nums[i];
                } else if(stack.get(i) == 0) {
                    result -= nums[i];
                }
            }
            if(result == S) ans += 1;
        } else {
            for(int i = 0; i < 2; i++) {
                stack.push(i);
                backtrack(nums, S, stack);
                stack.pop();
            }
        }
    }
}

其實回溯相當於暴力求解,將所有的可能都遍歷一遍,中間可能會加上剪枝的操作。

解法二:(動態規划-> 轉化為分割等和子集)

class Solution {
    public int findTargetSumWays(int[] nums, int S) {
        int sum = 0;
        for(int num : nums) {
            sum += num;
        }
        if(sum < S || (sum + S) % 2 != 0) return 0;
        return subSum(nums, (sum+S) >> 1);
    }

    public int subSum(int[] nums, int s) {
        int[] dp = new int[s+1];
        dp[0] = 1;
        for(int num : nums) {
            for(int i = s; i >= num; i--) {
                dp[i] += dp[i - num];
            }
        }
        return dp[s];
    }
}

66. leetcode 474. 一和零

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        if(strs.length == 0) return 0;
        int[][] dp = new int[m+1][n+1];

        for(String s : strs) {
            int ones = 0, zeros = 0;
            for(char c : s.toCharArray()) {
                if(c == '0'){
                    zeros++;
                } else {
                    ones++;
                }
            }

            for(int i = m; i >= zeros; i--) {
                for(int j = n; j >= ones; j--) {
                    dp[i][j] = Math.max(dp[i][j], dp[i-zeros][j-ones] + 1);
                }
            }
        }
        return dp[m][n];

    }
}

多維費用的背包問題。

67. leetcode 377. 組合總和IV

解法一:(回溯 ->超時)

class Solution {
    public int combinationSum4(int[] nums, int target) {
        backtrack(nums, target);
        return result;
    }

    public int result = 0;

    public void backtrack(int[] nums, int target) {
        if(target == 0) {
            result += 1;
            return;
        } else if(target > 0){
            for(int i = 0; i < nums.length; i++) {
                backtrack(nums, target - nums[i]);
            }
        } else {
            return;
        }
    }
}

發現有重復的子問題,用動態規划。

解法二:(動態規划)

class Solution {
    public int combinationSum4(int[] nums, int target) {
        int len = nums.length;
        if(len == 0) return 0;
        int[] dp = new int[target+1];
        dp[0] = 1; // 只有當nums中有一個數恰好等於target時才能遍歷到dp[0] 這時dp[0] = 1
        for(int i = 1; i <= target; i++) {
            for(int num : nums) {
                if(i - num >= 0) {
                    dp[i] += dp[i-num];
                }
            }
        }
        return dp[target];
    }
}

根據重疊子問題來發現使用動態規划,分析重疊子問題的情況。

狀態轉移方程:

dp[i] = sum(dp[i-num]);

但是自己覺得可以用選與不選的操作來做一做,不知道可不可行(背包的思想):

dp[i] = 選了的結果+沒選的結果

68. leetcode 583.兩個字符串的刪除操作

解法一:(遞歸)

class Solution {
    public int minDistance(String word1, String word2) {
        //尋找最長公共子序列
        int len1 = word1.length();
        int len2 = word2.length();
        if(len1 == 0 || len2 == 0) return Math.abs(len1 - len2);

        return len1+len2-lcs(word1, word2)*2;
    }

    public int lcs(String word1, String word2) {
        if(word1.length() == 0 || word2.length() == 0) return 0;
        if(word1.charAt(0) == word2.charAt(0)) {
            return lcs(word1.substring(1), word2.substring(1))+1;
        } else {
            return Math.max(lcs(word1.substring(1), word2), lcs(word1, word2.substring(1)));
        }
    }
}

從反方向思考發現本題是考察lcs,於是采取遞歸做法,發現超時,僅僅通過了25個測試用例。

有很多重疊子問題,於是采用動態規划。

解法二:(動態規划)

  public int minDistance(String word1, String word2) {
        //尋找最長公共子序列
        int len1 = word1.length();
        int len2 = word2.length();
        if(len1 == 0 || len2 == 0) return Math.abs(len1 - len2);
        int[][] dp = new int[len1+1][len2+1];
        for(int i = 1; i <= len1; i++) {
            for(int j = 1; j <= len2; j++) {
                if(word1.charAt(i-1) == word2.charAt(j-1)) {
                    dp[i][j] = dp[i-1][j-1]+1;
                } else {
                    dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
                }
            }
        }
       

        return len1+len2 - 2*dp[len1][len2];
    }

69. leetcode 38. 報數

class Solution {
    public String countAndSay(int n) {
        String tmp = "1";
        for(int i = 2; i <= n; i++) {
            StringBuilder sb = new StringBuilder();
            int pre = 0, next = 1; //采用雙指針
            int count = 1;
            while(next < tmp.length()) {
                if(tmp.charAt(pre) == tmp.charAt(next)) {
                   count++;
                   next++;
                } else {
                   sb.append(count).append(tmp.charAt(pre));
                   pre = next;
                   next++;
                   count = 1;
                }
            }
            sb.append(count).append(tmp.charAt(pre));
            tmp = sb.toString();  
        }
        return tmp;
        
    }
}

70. leetcode 32. 最長有效括號

解法一(暴力法)

class Solution {
    public int longestValidParentheses(String s) {
        int len = s.length();
        Stack<Character> stack = new Stack();
        if(len == 0) return 0;
        int maxLen = 0;
        for(int i = 0; i < len-1; i++) {
            stack.push(s.charAt(i));
            for(int j = i+1; j < len; j++) {
                if(stack.empty()) {stack.push(s.charAt(j));}
                else if( stack.peek() == ')') {
                    continue;
                    
                } else if(!isMatch(s.charAt(j)))  {
                    stack.push(s.charAt(j));

                }else {
                    stack.pop();
                }
                if(stack.isEmpty()) {
                    maxLen = Math.max(maxLen, j-i+1);
                }
            }
            while(!stack.empty()) {
                stack.pop();
            }
        }
        return maxLen;
    }

    public boolean isMatch(char t) {
        return  t==')';
    }
}

解法二(動態規划)

根據上面的暴力過程,可以發現有很多重疊子問題。但是狀態的定義以及狀態轉移很難想出來

class Solution {
    public int longestValidParentheses(String s) {
        int len = s.length();
        if(len == 0) return 0;
        int[] dp = new int[len];
        char[] sArray = s.toCharArray();
        int maxLen = 0;
        for(int i = 1; i < len; i++) {
            if(sArray[i] == ')' && sArray[i-1] == '(') {
                if(i-2 < 0) {
                    dp[i] = 2;
                } else {
                    dp[i] = dp[i-2] + 2;
                }
            } else if(sArray[i] == ')' && sArray[i-1] == ')') {
                if(i-dp[i-1]-1 >= 0 && sArray[i-dp[i-1]-1] == '(') {
                    dp[i] = i-dp[i-1]-2 >= 0 ? dp[i-1] + dp[i-dp[i-1]-2] +2 : dp[i-1] + 2;
                }
            }
            maxLen = Math.max(maxLen, dp[i]);
        }
        return maxLen;
    }
}

跟最長字符串的處理手段有點區別,兩個要好好對比。

寫動態規划的時候還要注意邊界條件的處理,會訪問到數組邊界外的情況。

71. leetcode 44. 通配符匹配

解法一(遞歸+備忘) 超時

class Solution {
    public boolean isMatch(String s, String p) {
        char[] ss = s.toCharArray();
        char[] pp = p.toCharArray();
        dp = new int[s.length() + 1][p.length() + 1];
        return back(ss, pp, 0, 0);
        
    }
    public int dp[][];
    public boolean back(char[] ss, char[] pp, int i, int j) {
        if (i == ss.length && j < pp.length && pp[j] == '*') {
            return back(ss, pp, i, j+1);
        }else if(i == ss.length && j != pp.length || i != ss.length && j == pp.length) {
            return false;
        } else if(i == ss.length && j == pp.length) {
            return true;
        }
        boolean ans = false;
        if(i < ss.length && j < pp.length && dp[i][j] != 0) return dp[i][j] > 0;
        if(ss[i] == pp[j] || pp[j] == '?') {
            ans = back(ss, pp, i+1, j+1);
        } else if (pp[j] == '*') {
            ans = back(ss, pp, i+1, j) || back(ss, pp, i, j+1);
        } else {
            dp[i][j] = ans ? 1 : -1;
        }
        return ans;
    }
}

普通遞歸以及備忘錄遞歸都導致超時,最耗時的是if(pp[j] == '*')這一塊。

解法二(動態規划)

class Solution {
    public boolean isMatch(String s, String p) {
        int lens = s.length();
        int lenp = p.length();
        boolean dp[][] = new boolean[lens+1][lenp+1]; // 處理s 和 p為空字符的特別好的手段
        // 很關鍵的初始化步驟
        dp[0][0] = true;
        for(int i = 1; i <= lenp; i++) {
            dp[0][i] = dp[0][i-1] && p.charAt(i-1) == '*';
        }

        for(int i = 1; i <= lens; i++) {
            for(int j = 1; j <= lenp; j++) {
                if(s.charAt(i-1) == p.charAt(j-1) || p.charAt(j-1) == '?') {
                    dp[i][j] = dp[i-1][j-1];
                } else if(p.charAt(j-1) == '*'){
                    dp[i][j] = dp[i][j-1] || dp[i-1][j];
                }
            }
        }
        return dp[lens][lenp];
    }
}

72. leetcode 72. 編輯距離

class Solution {
    public int minDistance(String word1, String word2) {
        int len1 = word1.length();
        int len2 = word2.length();
        int[][] dp = new int[len1+1][len2+1];
        for(int i = 1; i <= len1; i++) dp[i][0] = i;
        for(int i = 1; i <= len2; i++) dp[0][i] = i;

    // 對“dp[i-1][j-1] 表示替換操作,dp[i-1][j] 表示刪除操作,dp[i][j-1] 表示插入操作。”的補充理解:

    // 以 word1 為 "horse",word2 為 "ros",且 dp[5][3] 為例,即要將 word1的前 5 個字符轉換為 word2的前 3 個字符,也就是將 horse 轉換為 ros,因此有:

    // (1) dp[i-1][j-1],即先將 word1 的前 4 個字符 hors 轉換為 word2 的前 2 個字符 ro,然后將第五個字符 word1[4](因為下標基數以 0 開始) 由 e 替換為 s(即替換為 word2 的         第三個字符,word2[2])

    // (2) dp[i][j-1],即先將 word1 的前 5 個字符 horse 轉換為 word2 的前 2 個字符 ro,然后在末尾補充一個 s,即插入操作

    // (3) dp[i-1][j],即先將 word1 的前 4 個字符 hors 轉換為 word2 的前 3 個字符 ros,然后刪除 word1 的第 5 個字符
        for(int i = 1; i <= len1; i++) {
            for(int j = 1; j <= len2; j++) {
                if(word1.charAt(i-1) == word2.charAt(j-1)) {
                    dp[i][j] = dp[i-1][j-1];
                } else {
                    dp[i][j] = Math.min(dp[i-1][j-1], Math.min(dp[i-1][j], dp[i][j-1]))+1;
                } 
            }
        }
        return dp[len1][len2];
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM