题库笔记(Hard)

42. 接雨水

单调递减栈。

class Solution {
    public int trap(int[] height) {
        Deque<Integer> stack = new ArrayDeque<>();
        int res = 0;
        for (int i = 0; i < height.length; i++) {
            while (!stack.isEmpty() && height[stack.peek()] < height[i]) {
                int cur = stack.pop();
                if (stack.isEmpty()) {
                    break;
                }
                int h = Math.min(height[stack.peek()], height[i]) - height[cur];
                int w = i - stack.peek() - 1;
                res += h * w;
            }
            stack.push(i);
        }
        return res;
    }
}

4. 寻找两个正序数组的中位数

时间复杂度O(m+n),空间复杂度O(m+n)

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int len1 = nums1.length, len2 = nums2.length;
        int[] nums = new int[len1 + len2];
        int pos = 0, p = 0, q = 0;
        while (p < len1 && q < len2) {
            if (nums1[p] > nums2[q]) {
                nums[pos++] = nums2[q++];
            } else {
                nums[pos++] = nums1[p++];
            }
        }
        while (p < len1) {
            nums[pos++] = nums1[p++];
        }
        while (q < len2) {
            nums[pos++] = nums2[q++];
        }
        return (nums[(len1 + len2 - 1) / 2] + nums[(len1 + len2) / 2]) / 2.0;
    }
}

二分,时间复杂度Olog(m+n)

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int len1 = nums1.length, len2 = nums2.length;
        int left = (len1 + len2 + 1) / 2;
        int right = (len1 + len2 + 2) / 2;
        return (findKth(nums1, 0, nums2, 0, left) + findKth(nums1, 0, nums2, 0, right)) / 2.0;
    }

    private int findKth(int[] nums1, int begin1, int[] nums2, int begin2, int k) {
        if (begin1 >= nums1.length) {
            return nums2[begin2 + k - 1];
        }
        if (begin2 >= nums2.length) {
            return nums1[begin1 + k - 1];
        }
        if (k == 1) {
            return Math.min(nums1[begin1], nums2[begin2]);
        }
        int mid1 = (begin1 + k / 2 - 1 < nums1.length) ? nums1[begin1 + k / 2 - 1] : Integer.MAX_VALUE;
        int mid2 = (begin2 + k / 2 - 1 < nums2.length) ? nums2[begin2 + k / 2 - 1] : Integer.MAX_VALUE;
        if (mid1 < mid2) {
            return findKth(nums1, begin1 + k / 2, nums2, begin2, k - k / 2);
        } else {
            return findKth(nums1, begin1, nums2, begin2 + k / 2, k - k / 2);
        }
    }
}

25. K 个一组翻转链表

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode pre = dummy;
        while (head != null) {
            ListNode tail = pre;
            for (int i = 0; i < k; i++) {
                tail = tail.next;
                if (tail == null) {
                    return dummy.next;
                }
            }
            ListNode nextGroupHead = tail.next;
            //翻转head到tail的链表
            tail = head;//翻转之后头结点变成尾结点了
            ListNode dummy2 = new ListNode(0);
            dummy2.next = head;
            for (int i = 0; i < k; i++) {
                ListNode temp = head.next;
                head.next = dummy2.next;
                dummy2.next = head;
                head = temp;
            }
            head = dummy2.next;
            tail.next = nextGroupHead;//与下一组相连
            pre.next = head;//与上一组相连
            pre = tail;
            head = nextGroupHead;
        }
        return dummy.next;
    }
}

23. 合并K个升序链表

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        Queue<ListNode> pq = new PriorityQueue<>((o1, o2) -> o1.val - o2.val);
        for (ListNode node : lists) {
            if (node != null) {
                pq.offer(node);
            }
        }
        ListNode dummy = new ListNode(0);
        ListNode cur = dummy;
        while (!pq.isEmpty()) {
            ListNode node = pq.poll();
            cur.next = node;
            if (node.next != null) {
                pq.offer(node.next);
            }
            cur = cur.next;
        }
        return dummy.next;
    }
}

124. 二叉树中的最大路径和

class Solution {
    int res = Integer.MIN_VALUE;

    public int maxPathSum(TreeNode root) {
        dfs(root);
        return res;
    }

    private int dfs(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int left = Math.max(0, dfs(root.left));
        int right = Math.max(0, dfs(root.right));
        int sum = left + right + root.val;
        res = Math.max(sum, res);
        return root.val + Math.max(left, right);
    }
}

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 = 0; i <= len1; i++) {
            dp[i][0] = i;
        }
        for (int j = 0; j <= len2; j++) {
            dp[0][j] = j;
        }
        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] = 1 + Math.min(dp[i - 1][j], Math.min(dp[i][j - 1], dp[i - 1][j - 1]));
                }
            }
        }
        return dp[len1][len2];
    }
}

440. 字典序的第K小数字

class Solution {
    public int findKthNumber(int n, int k) {
        int cur = 1;
        while (k > 1) {
            long curNum = 0;//以cur为根的树的总结点数
            long first = cur;
            long next = cur + 1;
            while (first <= n) {
                curNum += Math.min((long) (n + 1), next) - first;
                first *= 10;
                next *= 10;
            }
            if (curNum < k) {//结点数小于k,说明在右边的树中
                cur++;
                k -= curNum;
            } else {//在cur的子树中
                k--;
                cur *= 10;
            }
        }
        return cur;
    }
}

32. 最长有效括号

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

精简:

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

10. 正则表达式匹配

比较难理解的是p的第j个数是的情况
如果s的第i个数等于p的第j-1个数 或者 p的第j-1个数是 .
dp[i][j] = dp[i-1][j] // a
记为多个a
or dp[i][j] = dp[i][j-1] // a* 记为单个a
or dp[i][j] = dp[i][j-2] // a*记为空
否则
dp[i][j] = dp[i][j-2]

class Solution {
    public boolean isMatch(String s, String p) {
        int sLen = s.length();
        int pLen = p.length();
        boolean[][] dp = new boolean[sLen + 1][pLen + 1];
        dp[0][0] = true;
        for (int j = 2; j <= pLen; j++) {
            if (p.charAt(j - 1) == '*') {
                dp[0][j] = dp[0][j - 2];
            }
        }
        for (int i = 1; i <= sLen; i++) {
            for (int j = 1; j <= pLen; j++) {
                if (s.charAt(i - 1) == p.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else if (p.charAt(j - 1) == '.') {
                    dp[i][j] = dp[i - 1][j - 1];
                } else if (p.charAt(j - 1) == '*') {
                    if (s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.') {
                        dp[i][j] = dp[i][j - 2] || dp[i - 1][j - 2] || dp[i - 1][j];
                    } else {
                        dp[i][j] = dp[i][j - 2];
                    }
                }
            }
        }
        return dp[sLen][pLen];
    }
}

135. 分发糖果

class Solution {
    public int candy(int[] ratings) {
        if (ratings.length == 0) {
            return 0;
        }
        int len = ratings.length;
        int[] left = new int[len], right = new int[len];
        Arrays.fill(left, 1);
        Arrays.fill(right, 1);
        for (int i = 1; i < len; i++) {
            if (ratings[i] > ratings[i - 1]) {
                left[i] = left[i - 1] + 1;
            }
        }
        for (int i = len - 2; i >= 0; i--) {
            if (ratings[i] > ratings[i + 1]) {
                right[i] = right[i + 1] + 1;
            }
        }
        int res = 0;
        for (int i = 0; i < len; i++) {
            res += Math.max(left[i], right[i]);
        }
        return res;
    }
}

改进,用一个数组:

class Solution {
    public int candy(int[] ratings) {
        int[] res = new int[ratings.length];
        Arrays.fill(res, 1);
        for (int i = 1; i < ratings.length; i++) {
            if (ratings[i] > ratings[i - 1]) {
                res[i] = res[i - 1] + 1;
            }
        }
        for (int i = ratings.length - 2; i >= 0; i--) {
            if (ratings[i] > ratings[i + 1]) {
                res[i] = Math.max(res[i], res[i + 1] + 1);
            }
        }
        int sum = 0;
        for (int i : res) {
            sum += i;
        }
        return sum;
    }
}

41. 缺失的第一个正数

class Solution {
    public int firstMissingPositive(int[] nums) {
        int len = nums.length;
        for (int i = 0; i < nums.length; i++) {
            while (nums[i] - 1 >= 0 && nums[i] - 1 < nums.length && nums[i] != nums[nums[i] - 1]) {
                swap(i, nums[i] - 1, nums);
            }
        }
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != i + 1) {
                return i + 1;
            }
        }
        return len + 1;
    }

    private void swap(int a, int b, int[] nums) {
        int temp = nums[a];
        nums[a] = nums[b];
        nums[b] = temp;
    }
}

76. 最小覆盖子串

滑动窗口

class Solution {
    public String minWindow(String s, String t) {
        int[] need = new int[128];
        int[] window = new int[128];
        String res = "";
        int minLen = Integer.MAX_VALUE;
        int left = 0, right = 0;
        int count = 0;
        for (char c : t.toCharArray()) {
            need[c]++;
        }
        while (right < s.length()) {
            char c = s.charAt(right);
            window[c]++;
            if (need[c] > 0 && window[c] <= need[c]) {
                count++;
            }
            while (count == t.length()) {
                char ch = s.charAt(left);
                window[ch]--;
                if (need[ch] > 0 && window[ch] < need[ch]) {
                    count--;
                }
                if (right - left + 1 < minLen) {
                    minLen = right - left + 1;
                    res = s.substring(left, right + 1);
                }
                left++;
            }
            right++;
        }
        return res;
    }
}

84. 柱状图中最大的矩形

单调递增栈。对于出栈的元素cur,当前遍历的元素i是右边第一个比它矮的柱子,栈顶元素peek是左边第一个比它矮的柱子,对于高为heights[cur]的最大矩形面积已可以计算,宽等于 i - stack.peek() - 1。

class Solution {
    public int largestRectangleArea(int[] heights) {
        int res = 0;
        Deque<Integer> stack = new ArrayDeque<>();
        int[] newHeights = new int[heights.length + 2];
        newHeights[0] = newHeights[heights.length + 1] = 0;
        System.arraycopy(heights, 0, newHeights, 1, heights.length);
        heights = newHeights;
        for (int i = 0; i < heights.length; i++) {
            while (!stack.isEmpty() && heights[i] < heights[stack.peek()]) {
                int cur = stack.pop();
                int h = heights[cur];
                int w = i - stack.peek() - 1;
                res = Math.max(res, h * w);
            }
            stack.push(i);
        }
        return res;
    }
}

128. 最长连续序列

class Solution {
    int[] father;
    int[] size;
    int res = 1;

    private void init() {
        for (int i = 0; i < father.length; i++) {
            father[i] = i;
            size[i] = 1;
        }
    }

    private int findFather(int x) {
        while (father[x] != x) {
            x = father[x];
        }
        return x;
    }

    private void union(int a, int b) {
        int fA = findFather(a);
        int fB = findFather(b);
        if (fA != fB) {
            father[fA] = fB;
            size[fB] += size[fA];
            res = Math.max(res, size[fB]);
        }
    }

    public int longestConsecutive(int[] nums) {
        if (nums.length == 0) {
            return 0;
        }
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            map.put(nums[i], i);
        }
        father = new int[nums.length];
        size = new int[nums.length];
        init();
        for (int i : nums) {
            if (map.containsKey(i + 1)) {
                union(map.get(i), map.get(i + 1));
            }
        }
        return res;
    }
}

45. 跳跃游戏 II

class Solution {
    public int jump(int[] nums) {
        int res = 0;
        int maxPos = 0;//当前最远可到达的位置
        int end = 0;//当前要到达的位置
        int curPos;//当前的位置
        for (curPos = 0; curPos < nums.length - 1; curPos++) {
            maxPos = Math.max(maxPos, curPos + nums[curPos]);
            if (curPos == end) {
                res++;
                end = maxPos;
            }
        }
        return res;
    }
}

85. 最大矩形

转化成第84题。


class Solution {
    private int cal(int[] heights) {
        int[] newHeights = new int[heights.length + 2];
        newHeights[0] = newHeights[heights.length + 1] = 0;
        System.arraycopy(heights, 0, newHeights, 1, heights.length);
        heights = newHeights;
        Deque<Integer> stack = new ArrayDeque<>();
        int res = 0;
        for (int i = 0; i < heights.length; i++) {
            while (!stack.isEmpty() && heights[i] < heights[stack.peek()]) {
                int cur = stack.pop();
                int h = heights[cur];
                int w = i - stack.peek() - 1;
                res = Math.max(res, h * w);
            }
            stack.push(i);
        }
        return res;
    }

    public int maximalRectangle(char[][] matrix) {
        if (matrix.length == 0) {
            return 0;
        }
        int[] heights = new int[matrix[0].length];
        int res = 0;
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                if (matrix[i][j] == '0') {
                    heights[j] = 0;
                } else {
                    heights[j]++;
                }
            }
            res = Math.max(res, cal(heights));
        }
        return res;
    }
}

51. N 皇后

class Solution {
    List<List<String>> res = new ArrayList<>();
    int[] arr;
    boolean[] isVisited;

    private boolean judge(int n) {
        boolean flag = true;
        label:
        for (int i = 1; i <= n; i++) {//第i行的皇后和后面所有行的皇后判断是否在对角线上
            for (int j = i + 1; j <= n; j++) {
                if (Math.abs(i - j) == Math.abs(arr[i] - arr[j])) {
                    flag = false;
                    break label;
                }
            }
        }
        return flag;

    }

    private void dfs(int begin, int n) {
        if (begin == n + 1) {
            if (judge(n) == true) {
                List<String> list = new ArrayList<>();
                for (int i = 1; i <= n; i++) {
                    StringBuilder sb = new StringBuilder();
                    for (int j = 1; j <= n; j++) {
                        if (j == arr[i]) {
                            sb.append('Q');
                        } else {
                            sb.append('.');
                        }
                    }
                    list.add(sb.toString());
                }
                res.add(list);
            }
            return;
        }
        for (int i = 1; i <= n; i++) {
            if (isVisited[i] == false) {
                arr[begin] = i;
                isVisited[i] = true;
                dfs(begin + 1, n);
                isVisited[i] = false;
            }
        }
    }

    public List<List<String>> solveNQueens(int n) {
        arr = new int[n + 1];
        isVisited = new boolean[n + 1];
        dfs(1, n);
        return res;
    }
}

329. 矩阵中的最长递增路径

记忆化dfs:
由于同一个单元格对应的最长递增路径的长度是固定不变的,因此可以使用记忆化的方法进行优化。用矩阵memo作为缓存矩阵,已经计算过的单元格的结果存储到缓存矩阵中。

class Solution {
    int[] dirX = {1, -1, 0, 0};
    int[] dirY = {0, 0, 1, -1};
    int[][] memo;

    private int dfs(int i, int j, int[][] matrix) {
        if (memo[i][j] != 0) {
            return memo[i][j];
        }
        memo[i][j]++;
        for (int k = 0; k < 4; k++) {
            int newI = i + dirX[k];
            int newJ = j + dirY[k];
            if (newI >= 0 && newI < matrix.length && newJ >= 0 && newJ < matrix[0].length
                    && matrix[newI][newJ] > matrix[i][j]) {
                memo[i][j] = Math.max(memo[i][j], dfs(newI, newJ, matrix) + 1);
            }
        }
        return memo[i][j];
    }

    public int longestIncreasingPath(int[][] matrix) {
        if (matrix.length == 0) {
            return 0;
        }
        memo = new int[matrix.length][matrix[0].length];
        int res = 0;
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                res = Math.max(res, dfs(i, j, matrix));
            }
        }
        return res;
    }
}

297. 二叉树的序列化与反序列化

public class Codec {

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        if (root == null) {
            return "null";
        }
        StringBuilder res = new StringBuilder();
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        while (!q.isEmpty()) {
            int size = q.size();
            for (int i = 0; i < size; i++) {
                TreeNode front = q.poll();
                if (front == null) {
                    res.append("null,");
                    continue;
                } else {
                    res.append(front.val + ",");
                    q.offer(front.left);
                    q.offer(front.right);
                }
            }
        }
        res.deleteCharAt(res.length() - 1);
        return res.toString();
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if ("null".equals(data)) {
            return null;
        }
        String[] splits = data.split(",");
        TreeNode root = new TreeNode(Integer.parseInt(splits[0]));
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        for (int i = 1; i < splits.length; i++) {
            TreeNode front = q.poll();
            if (!"null".equals(splits[i])) {
                front.left = new TreeNode(Integer.parseInt(splits[i]));
                q.offer(front.left);
            }
            i++;
            if (!"null".equals(splits[i])) {
                front.right = new TreeNode(Integer.parseInt(splits[i]));
                q.offer(front.right);
            }
        }
        return root;
    }
}

354. 俄罗斯套娃信封问题

class Solution {
    private int lis(int[][] envelopes) {
        int[] dp = new int[envelopes.length];
        dp[0] = 1;
        int res = 1;
        for (int i = 1; i < envelopes.length; i++) {
            dp[i] = 1;
            for (int j = 0; j < i; j++) {
                if (envelopes[j][1] < envelopes[i][1] && envelopes[j][0] < envelopes[i][0]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            res = Math.max(res, dp[i]);
        }
        return res;
    }

    public int maxEnvelopes(int[][] envelopes) {
        if (envelopes.length == 0) {
            return 0;
        }
        Arrays.sort(envelopes, (o1, o2) -> o1[0] - o2[0]);
        return lis(envelopes);
    }
}

剑指 Offer 51. 数组中的逆序对

归并排序代码:

    public void mergeSort(int[] arr, int lo, int hi) {
        if (lo >= hi) {
            return;
        }
        int mid = lo + (hi - lo) / 2;
        mergeSort(arr, lo, mid);
        mergeSort(arr, mid + 1, hi);
        int[] temp = new int[hi - lo + 1];
        int i = lo, j = mid + 1, pos = 0;
        while (i <= mid && j <= hi) {
            temp[pos++] = arr[i] < arr[j] ? arr[i++] : arr[j++];
        }
        while (i <= mid) {
            temp[pos++] = arr[i++];
        }
        while (j <= hi) {
            temp[pos++] = arr[j++];
        }
        System.arraycopy(temp, 0, arr, lo, pos);
    }

在并的过程中计算逆序对:

class Solution {
    public int reversePairs(int[] nums) {
        return mergeSort(nums, 0, nums.length - 1);
    }

    public int mergeSort(int[] arr, int lo, int hi) {
        if (lo >= hi) {
            return 0;
        }
        int mid = lo + (hi - lo) / 2;
        int res = mergeSort(arr, lo, mid) + mergeSort(arr, mid + 1, hi);
        int[] temp = new int[hi - lo + 1];
        int i = lo, j = mid + 1, pos = 0;
        while (i <= mid && j <= hi) {
            res += arr[i] <= arr[j] ? 0 : mid + 1 - i;//arr[j]小,从i到mid的所有数与j构成逆序对f
            temp[pos++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
        }
        while (i <= mid) {
            temp[pos++] = arr[i++];
        }
        while (j <= hi) {
            temp[pos++] = arr[j++];//此时arr[j]比左边的所有数都大了,不可能构成逆序对
        }
        System.arraycopy(temp, 0, arr, lo, pos);
        return res;
    }
}

460. LFU 缓存


239. 滑动窗口最大值

双端队列:
从队尾到队头以元素值递增的顺序保存数组的下标,队尾的数最小,队头的数最大。
addLast是往队尾加,addFirst是往队头加。

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int[] res = new int[nums.length - k + 1];
        Deque<Integer> q = new ArrayDeque<>();
        for (int i = 0; i < nums.length; i++) {
            //如果队尾的数小于等于nums[i],队尾出队。
            while (!q.isEmpty() && nums[q.peekLast()] <= nums[i]) {
                q.pollLast();
            }
            q.addLast(i);
            if (q.peek() <= i - k) {//如果队头的值不在窗口内,队头出队
                q.poll();
            }
            if (i - k + 1 >= 0) {
                res[i - k + 1] = nums[q.peek()];//队头保存的是窗口中的最大值
            }
        }
        return res;
    }
}

726. 原子的数量

44. 通配符匹配

class Solution {
    public boolean isMatch(String s, String p) {
        int sLen = s.length();
        int pLen = p.length();
        boolean[][] dp = new boolean[sLen + 1][pLen + 1];
        dp[0][0] = true;
        for (int j = 1; j <= pLen; j++) {
            if (p.charAt(j - 1) == '*') {
                dp[0][j] = true;
            } else {
                break;
            }
        }
        for (int i = 1; i <= sLen; i++) {
            for (int j = 1; j <= pLen; j++) {
                if (s.charAt(i - 1) == p.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else if (p.charAt(j - 1) == '?') {
                    dp[i][j] = dp[i - 1][j - 1];
                } else if (p.charAt(j - 1) == '*') {
                    dp[i][j] = dp[i - 1][j] || dp[i][j - 1];
                }
            }
        }
        return dp[sLen][pLen];
    }
}

123. 买卖股票的最佳时机 III

class Solution {
    public int maxProfit(int[] prices) {
        if (prices.length == 0) {
            return 0;
        }
        int[][][] dp = new int[prices.length][3][2];
        for (int j = 1; j <= 2; j++) {
            dp[0][j][1] = -prices[0];
        }
        for (int i = 1; i < prices.length; i++) {
            for (int j = 1; j <= 2; j++) {
                dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);
                dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);
            }
        }
        return dp[prices.length - 1][2][0];
    }
}

97. 交错字符串

class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {
        int len1 = s1.length();
        int len2 = s2.length();
        if (len1 + len2 != s3.length()) {
            return false;
        }
        //dp[i][j]代表s1的前i个字符与s2的前j个字符是否可以交错组成s3的前i+j个字符
        boolean[][] dp = new boolean[len1 + 1][len2 + 1];
        dp[0][0] = true;
        for (int i = 1; i <= len1; i++) {
            if (s1.charAt(i - 1) == s3.charAt(i - 1)) {
                dp[i][0] = true;
            } else {
                break;
            }
        }
        for (int j = 1; j <= len2; j++) {
            if (s2.charAt(j - 1) == s3.charAt(j - 1)) {
                dp[0][j] = true;
            } else {
                break;
            }
        }
        for (int i = 1; i <= len1; i++) {
            for (int j = 1; j <= len2; j++) {
                if (s1.charAt(i - 1) == s3.charAt(i + j - 1) && dp[i - 1][j]) {
                    dp[i][j] = true;
                } else if (s2.charAt(j - 1) == s3.charAt(i + j - 1) && dp[i][j - 1]) {
                    dp[i][j] = true;
                }
            }
        }
        return dp[len1][len2];
    }
}

887. 鸡蛋掉落

public class Solution {

    public int superEggDrop(int K, int N) {

        // dp[i][j]:一共有 i 层楼梯的情况下,使用 j 个鸡蛋的最少实验的次数
        // 注意:
        // 1、i 表示的是楼层的大小,不是第几层的意思,例如楼层区间 [8, 9, 10] 的大小为 3,这一点是在状态转移的过程中调整的定义
        // 2、j 表示可以使用的鸡蛋的个数,它是约束条件,我个人习惯放在后面的维度,表示消除后效性的意思

        // 0 个楼层和 0 个鸡蛋的情况都需要算上去,虽然没有实际的意义,但是作为递推的起点,被其它状态值所参考
        int[][] dp = new int[N + 1][K + 1];

        // 由于求的是最小值,因此初始化的时候赋值为一个较大的数,9999 或者 i 都可以
        for (int i = 0; i <= N; i++) {
            Arrays.fill(dp[i], i);
        }

        // 初始化:填写下标为 0、1 的行和下标为 0、1 的列
        // 第 0 行:楼层为 0 的时候,不管鸡蛋个数多少,都测试不出鸡蛋的 F 值,故全为 0
        for (int j = 0; j <= K; j++) {
            dp[0][j] = 0;
        }

        // 第 1 行:楼层为 1 的时候,0 个鸡蛋的时候,扔 0 次,1 个以及 1 个鸡蛋以上只需要扔 1 次
        dp[1][0] = 0;
        for (int j = 1; j <= K; j++) {
            dp[1][j] = 1;
        }

        // 第 0 列:鸡蛋个数为 0 的时候,不管楼层为多少,也测试不出鸡蛋的 F 值,故全为 0
        // 第 1 列:鸡蛋个数为 1 的时候,这是一种极端情况,要试出 F 值,最少次数就等于楼层高度(想想复杂度的定义)
        for (int i = 0; i <= N; i++) {
            dp[i][0] = 0;
            dp[i][1] = i;
        }

        // 从第 2 行,第 2 列开始填表
        for (int i = 2; i <= N; i++) {
            for (int j = 2; j <= K; j++) {
                for (int k = 1; k <= i; k++) {
                    // 碎了,就需要往低层继续扔:层数少 1 ,鸡蛋也少 1
                    // 不碎,就需要往高层继续扔:层数是当前层到最高层的距离差,鸡蛋数量不少
                    // 两种情况都做了一次尝试,所以加 1
                    dp[i][j] = Math.min(dp[i][j], Math.max(dp[k - 1][j - 1], dp[i - k][j]) + 1);
                }
            }
        }
        return dp[N][K];
    }
}
public class Solution {
    private int cal(int K, int T) {
        if (T == 1 || K == 1) {
            return T + 1;
        }
        return cal(K - 1, T - 1) + cal(K, T - 1);
    }

    public int superEggDrop(int K, int N) {
        int T = 1;
        while (cal(K, T) < N + 1) {
            T++;
        }
        return T;
    }
}

315. 计算右侧小于当前元素的个数

class Solution {
    int[] res;
    int[] index;//当前位置的数对应原数组的第几个位置

    public List<Integer> countSmaller(int[] nums) {
        res = new int[nums.length];
        index = new int[nums.length];
        for (int i = 0; i < index.length; i++) {
            index[i] = i;
        }
        mergeSort(nums, 0, nums.length - 1);
        List<Integer> r = Arrays.stream(res).boxed().collect(Collectors.toList());
        return r;
    }

    private void mergeSort(int[] nums, int lo, int hi) {
        if (lo >= hi) {
            return;
        }
        int mid = lo + (hi - lo) / 2;
        mergeSort(nums, lo, mid);
        mergeSort(nums, mid + 1, hi);
        int[] temp = new int[hi - lo + 1];
        int[] tempindex = new int[hi - lo + 1];
        int i = lo, j = mid + 1, pos = 0;
        while (i <= mid && j <= hi) {
            res[index[i]] += nums[i] <= nums[j] ? j - mid - 1 : 0;
            tempindex[pos] = nums[i] <= nums[j] ? index[i] : index[j];
            temp[pos++] = nums[i] <= nums[j] ? nums[i++] : nums[j++];
        }
        while (i <= mid) {
            res[index[i]] += j - mid - 1;
            tempindex[pos] = index[i];
            temp[pos++] = nums[i++];
        }
        while (j <= hi) {
            tempindex[pos] = index[j];
            temp[pos++] = nums[j++];
        }
        System.arraycopy(tempindex, 0, index, lo, pos);
        System.arraycopy(temp, 0, nums, lo, pos);
    }
}

493. 翻转对


class Solution {
    public int reversePairs(int[] nums) {
        return mergeSort(nums, 0, nums.length - 1);
    }

    private int mergeSort(int[] nums, int lo, int hi) {
        if (lo >= hi) {
            return 0;
        }
        int mid = lo + (hi - lo) / 2;
        int res = 0;
        res += mergeSort(nums, lo, mid) + mergeSort(nums, mid + 1, hi);
        int[] temp = new int[hi - lo + 1];
        int i = lo, j = mid + 1, pos = 0;
        //先单独统计,因为合并的时候统计不了
        while (i <= mid && j <= hi) {
            if ((long) nums[i] > 2 * (long) nums[j]) {
                res += mid - i + 1;
                j++;
            } else {
                i++;
            }
        }
        i = lo;
        j = mid + 1;
        while (i <= mid && j <= hi) {
            temp[pos++] = nums[i] <= nums[j] ? nums[i++] : nums[j++];
        }
        while (i <= mid) {
            temp[pos++] = nums[i++];
        }
        while (j <= hi) {
            temp[pos++] = nums[j++];
        }
        System.arraycopy(temp, 0, nums, lo, pos);
        return res;
    }
}

407. 接雨水 II

class Solution {
    public int trapRainWater(int[][] heightMap) {
        if (heightMap.length == 0) {
            return 0;
        }
        Queue<int[]> pq = new PriorityQueue<>((o1, o2) -> o1[2] - o2[2]);
        int row = heightMap.length;
        int col = heightMap[0].length;
        boolean[][] isVisited = new boolean[row][col];
        int res = 0;
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (i == 0 || i == row - 1 || j == 0 || j == col - 1) {
                    pq.offer(new int[]{i, j, heightMap[i][j]});
                    isVisited[i][j] = true;
                }
            }
        }
        int[] dirX = {1, -1, 0, 0};
        int[] dirY = {0, 0, 1, -1};
        while (!pq.isEmpty()) {
            int[] front = pq.poll();
            for (int i = 0; i < 4; i++) {
                int nx = front[0] + dirX[i];
                int ny = front[1] + dirY[i];
                if (nx >= 0 && nx < row && ny >= 0 && ny < col && isVisited[nx][ny] == false) {
                    if (heightMap[nx][ny] < front[2]) {
                        res += front[2] - heightMap[nx][ny];
                    }
                    pq.offer(new int[]{nx, ny, Math.max(heightMap[nx][ny], front[2])});
                    isVisited[nx][ny] = true;
                }
            }
        }
        return res;
    }
}

295. 数据流的中位数

class MedianFinder {

    Queue<Integer> maxHeap;
    Queue<Integer> minHeap;
    int size = 0;

    /**
     * initialize your data structure here.
     */
    public MedianFinder() {
        this.minHeap = new PriorityQueue<>();
        this.maxHeap = new PriorityQueue<>((o1, o2) -> o2.compareTo(o1));
    }

    public void addNum(int num) {
        size++;
        maxHeap.add(num);
        minHeap.add(maxHeap.poll());
        if (size % 2 == 1) {
            maxHeap.add(minHeap.poll());
        }
    }

    public double findMedian() {
        if (size % 2 == 1) {
            return maxHeap.peek();
        } else {
            return (maxHeap.peek() + minHeap.peek()) / 2.0;
        }
    }
}

188. 买卖股票的最佳时机 IV

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

862. 和至少为 K 的最短子数组

312. 戳气球

class Solution {
    public int maxCoins(int[] nums) {
        int[] newNums = new int[nums.length + 2];
        newNums[0] = newNums[nums.length + 1] = 1;//注意这里是1,不是0
        System.arraycopy(nums, 0, newNums, 1, nums.length);
        nums = newNums;
        int[][] dp = new int[nums.length][nums.length];
        for (int L = 3; L <= nums.length; L++) {
            for (int i = 0; i + L - 1 < nums.length; i++) {
                int j = i + L - 1;
                for (int k = i + 1; k < j; k++) {
                    dp[i][j] = Math.max(dp[i][j], dp[i][k] + dp[k][j] + nums[i] * nums[k] * nums[j]);
                }
            }
        }
        return dp[0][nums.length - 1];
    }
}

224. 基本计算器

class Solution {
    Map<String, Integer> inPriority, outPriority;

    public Solution() {
        inPriority = new HashMap<>();//栈内优先级
        outPriority = new HashMap<>();//栈外优先级
        inPriority.put("#", 0);
        inPriority.put("(", 1);
        inPriority.put("*", 5);
        inPriority.put("/", 5);
        inPriority.put("+", 3);
        inPriority.put("-", 3);
        inPriority.put(")", 6);
        outPriority.put("#", 0);
        outPriority.put("(", 6);
        outPriority.put("*", 4);
        outPriority.put("/", 4);
        outPriority.put("+", 2);
        outPriority.put("-", 2);
        outPriority.put(")", 1);
    }

    public int calculate(String s) {
        List<String> infix = new ArrayList<>();//中缀表达式
        boolean flag = false;
        int num = 0;
        for (char c : s.toCharArray()) {
            if (c == ' ') {
                continue;
            } else if (Character.isDigit(c)) {
                flag = true;
                num = num * 10 + (c - '0');
            } else {
                if (flag == true) {
                    infix.add(String.valueOf(num));
                    flag = false;
                    num = 0;
                }
                infix.add(String.valueOf(c));
            }
        }
        if (flag == true) {
            infix.add(String.valueOf(num));
        }
        List<String> rpn = getRpn(infix);
        return (int) cal(rpn);
    }

    //中缀表达式转后缀
    public List<String> getRpn(List<String> infix) {
        Deque<String> stack = new ArrayDeque<>();
        stack.push("#");
        List<String> rpn = new ArrayList<>();
        for (String str : infix) {
            if ("(".equals(str) || ")".equals(str) || "+".equals(str) || "-".equals(str) ||
                    "*".equals(str) || "/".equals(str)) {
                if (comparePriority(stack.peek(), str) == 1) {
                    stack.push(str);
                } else if (comparePriority(stack.peek(), str) == 0) {
                    stack.pop();
                } else {
                    while (comparePriority(stack.peek(), str) == -1) {
                        String z = stack.pop();
                        rpn.add(z);
                    }
                    if (")".equals(str)) {
                        stack.pop();
                    } else {
                        stack.push(str);
                    }
                }
            } else {
                rpn.add(str);
            }
        }
        while (!stack.isEmpty()) {
            rpn.add(stack.pop());
        }
        rpn.remove(rpn.size() - 1);
        return rpn;
    }

    private int comparePriority(String in, String out) {
        if (outPriority.get(out) > inPriority.get(in)) {
            return 1;
        } else if (outPriority.get(out) == inPriority.get(in)) {
            return 0;
        } else {
            return -1;
        }
    }

    //后缀表达式求值
    public double cal(List<String> s) {
        Deque<Double> stack = new ArrayDeque<>();
        for (int i = 0; i < s.size(); i++) {
            double a, b;
            if (s.get(i).equals("+")) {
                b = stack.pop();
                a = stack.pop();
                stack.push(a + b);
                continue;
            } else if (s.get(i).equals("-")) {
                b = stack.pop();
                a = stack.pop();
                stack.push(a - b);
                continue;
            } else if (s.get(i).equals("*")) {
                b = stack.pop();
                a = stack.pop();
                stack.push(a * b);
                continue;
            } else if (s.get(i).equals("/")) {
                b = stack.pop();
                a = stack.pop();
                stack.push(a / b);
                continue;
            } else {
                stack.push(Double.valueOf(s.get(i)));
            }
        }
        return stack.peek();
    }
}

403. 青蛙过河

dp[i]代表可以以大小为jumpsize的一跳跳到第i个位置的集合。
边界:
dp[0]的集合只有一个元素0,只需要0跳就可以到达第一个位置。
转移方程:
遍历集合,那么下一跳可以跳的步数为step-1 ~ step+1,判断可以到达的位置是否有石块,如果有,就把那个石块对应的集合增加这一跳的步数。
最后判断最后一个石头的集合是否为空即可。如果不为空,说明可以跳到这里来。

class Solution {
    public boolean canCross(int[] stones) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < stones.length; i++) {
            map.put(stones[i], i);
        }
        Set<Integer>[] dp = new HashSet[stones.length];
        for (int i = 0; i < dp.length; i++) {
            dp[i] = new HashSet<>();
        }
        dp[0].add(0);
        for (int i = 0; i < stones.length; i++) {
            for (int j : dp[i]) {
                for (int step = j - 1; step <= j + 1; step++) {
                    if (step > 0 && map.containsKey(stones[i] + step)) {
                        dp[map.get(stones[i] + step)].add(step);
                    }
                }
            }
        }
        return dp[stones.length - 1].size() > 0;
    }
}

679. 24 点游戏


class Solution {
    private boolean dfs(List<Double> list) {
        if (list.size() == 1) {
            return Math.abs(list.get(0) - 24) < 1e-6;
        }
        for (int i = 0; i < list.size(); i++) {
            for (int j = 0; j < list.size(); j++) {
                if (i == j) {
                    continue;
                }
                List<Double> newList = new ArrayList<>();
                for (int k = 0; k < list.size(); k++) {
                    if (k != i && k != j) {
                        newList.add(list.get(k));
                    }
                }
                for (int z = 0; z < 4; z++) {
                    if (z == 0) {
                        newList.add(list.get(i) + list.get(j));
                    } else if (z == 1) {
                        newList.add(list.get(i) - list.get(j));
                    } else if (z == 2) {
                        newList.add(list.get(i) * list.get(j));
                    } else if (z == 3) {
                        if (list.get(j) == 0) {
                            continue;
                        }
                        newList.add(list.get(i) / list.get(j));
                    }
                    if (dfs(newList)) {
                        return true;
                    } else {
                        newList.remove(newList.size() - 1);
                    }
                }
            }
        }
        return false;
    }

    public boolean judgePoint24(int[] nums) {
        List<Double> list = new ArrayList<>();
        for (int i : nums) {
            list.add((double) i);
        }
        return dfs(list);
    }
}

1044. 最长重复子串

class Solution {
    long mod = (long)Math.pow(2, 37) - 1;
    long a = 26;
    int nums[];
    int n;
    public String longestDupSubstring(String S) {
        nums = new int [S.length()];
        for (int i = 0; i < S.length(); i ++) {
            nums[i] = S.charAt(i) - 'a';
        }
        this.n = S.length();
        int left = 1;
        int right = n;
        while (left != right) {
            int mid = left + (right - left) / 2;
            if (search(mid) != -1) left = mid + 1;
            else right = mid;
        }
        int begin = search(left - 1);
        if (begin != -1) return S.substring(begin, begin + left - 1);
        return "";
    }

    public int search(int length) {
        long cur = 0;
        for (int i = 0; i < length; i ++) {
            cur = (cur * a + nums[i]) % mod;
        }
        long aL = 1;
        for (int i = 0; i < length; i++) {
            aL = (aL * a ) % mod;
        }
        HashSet<Long> hs = new HashSet<>();
        hs.add(cur);

        for (int i = 1; i <= n - length; i ++) {
            cur = (cur * a - aL * nums[i - 1] % mod + mod) % mod;
            cur = (cur + nums[i + length - 1]) % mod;
            if (hs.contains(cur)) return i;
            hs.add(cur);
        }
        return -1;
    }
}

410. 分割数组的最大值

dp[i][j]代表数组的前i个数分成m个子数组,和的最大值最小。
边界:
dp[0][0] = 0
转移方程:


class Solution {
    public int splitArray(int[] nums, int m) {
        int n = nums.length;
        int[][] dp = new int[n + 1][m + 1];
        for (int i = 0; i < dp.length; i++) {
            Arrays.fill(dp[i], Integer.MAX_VALUE);
        }
        int[] preSum = new int[n + 1];
        for (int i = 0; i < preSum.length - 1; i++) {
            preSum[i + 1] = preSum[i] + nums[i];
        }
        dp[0][0] = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= Math.min(i, m); j++) {
                for (int k = 0; k < i; k++) {
                    dp[i][j] = Math.min(dp[i][j], Math.max(dp[k][j - 1], preSum[i] - preSum[k]));
                }
            }
        }
        return dp[n][m];
    }
}

60. 排列序列


class Solution {
    boolean[] isVisited;
    StringBuilder temp = new StringBuilder();
    String res = null;
    int count = 0;

    private void dfs(int n, int k) {
        if (temp.length() == n) {
            count++;
            if (count == k) {
                res = temp.toString();
            }
            return;
        }
        for (int i = 1; i <= n; i++) {
            if (isVisited[i] == false) {
                isVisited[i] = true;
                temp.append(i);
                dfs(n, k);
                if (res != null) {
                    return;
                }
                isVisited[i] = false;
                temp.deleteCharAt(temp.length() - 1);
            }
        }
    }

    public String getPermutation(int n, int k) {
        isVisited = new boolean[n + 1];
        dfs(n, k);
        return res;
    }
}

37. 解数独


class Solution {
    private boolean dfs(char[][] board) {
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                if (board[i][j] == '.') {
                    for (char c = '1'; c <= '9'; c++) {
                        if (check(i, j, c, board)) {
                            board[i][j] = c;
                            if (dfs(board)) {
                                return true;
                            } else {
                                board[i][j] = '.';
                            }
                        }
                    }
                    return false;
                }
            }
        }
        return true;
    }

    private boolean check(int row, int col, char c, char[][] board) {
        int blockRow = 3 * (row / 3);
        int blockCol = 3 * (col / 3);
        for (int i = 0; i < board.length; i++) {
            if (board[i][col] == c) {
                return false;
            }
            if (board[row][i] == c) {
                return false;
            }
            if (board[blockRow + i / 3][blockCol + i % 3] == c) {
                return false;
            }
        }
        return true;
    }

    public void solveSudoku(char[][] board) {
        dfs(board);
    }
}

30. 串联所有单词的子串

暴力法,先计算所有子串的长度,然后不断截取这个长度的字符串,判断是否和words匹配。

class Solution {
    Map<String, Integer> map = new HashMap<>();

    private boolean judge(String s, String[] words) {
        Map<String, Integer> temp = new HashMap<>();
        int wordLen = words[0].length();
        for (int i = 0; i < words.length; i++) {
            String sub = s.substring(i * wordLen, i * wordLen + wordLen);
            temp.put(sub, temp.getOrDefault(sub, 0) + 1);
        }
        for (String key : temp.keySet()) {
            if (!temp.get(key).equals(map.get(key))) {
                return false;
            }
        }
        return true;
    }

    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> res = new ArrayList<>();
        if ("".equals(s)) {
            return res;
        }
        for (String word : words) {
            map.put(word, map.getOrDefault(word, 0) + 1);
        }
        int len = words.length * words[0].length();
        for (int i = 0; i + len - 1 < s.length(); i++) {
            if (judge(s.substring(i, i + len), words)) {
                res.add(i);
            }
        }
        return res;
    }
}

132. 分割回文串 II

dp[i]代表以位置i结尾的字符串的最少分割次数。
边界:
dp[i] = i
转移方程:
遍历字符串,如果以位置i为结尾的字符串就是回文串,直接令dp[i] = 0,然后继续遍历。如果不是,那么遍历j从0到i-1,只要从j到i的子串是回文串,那么dp[i] = dp[j] +1,在遍历j的过程中取dp[i]的最小值。

class Solution {
    private boolean isPar(String s) {
        int lo = 0, hi = s.length() - 1;
        while (lo < hi) {
            if (s.charAt(lo++) != s.charAt(hi--)) {
                return false;
            }
        }
        return true;
    }

    public int minCut(String s) {
        int[] dp = new int[s.length()];
        for (int i = 0; i < s.length(); i++) {
            dp[i] = i;
        }
        for (int i = 1; i < s.length(); i++) {
            if (isPar(s.substring(0, i + 1))) {
                dp[i] = 0;
                continue;
            }
            for (int j = 0; j < i; j++) {
                if (isPar(s.substring(j + 1, i + 1))) {
                    dp[i] = Math.min(dp[i], dp[j] + 1);
                }
            }
        }
        return dp[s.length() - 1];
    }
}

57. 插入区间

判断两个区间是否相交的方法:
左端点的最大值小于等于右端点的最小值,则相交。
遍历原有区间,只要与新插入区间相交,则更新插入的区间,左端点更新为两个区间左端点的较小值,右端点更新为两个区间右端点的较大值,不加到结果集。
如果不相交,则可以加到结果集中。
最后将新区间加到结果集中,然后按照左端点从小到大排序。

class Solution {
    //判断两个区间是否相交
    private boolean check(int[] a, int[] b) {
        return Math.max(a[0], b[0]) <= Math.min(a[1], b[1]);
    }

    public int[][] insert(int[][] intervals, int[] newInterval) {
        List<int[]> res = new ArrayList<>();
        for (int[] interval : intervals) {
            if (check(interval, newInterval)) {
                newInterval[0] = Math.min(interval[0], newInterval[0]);
                newInterval[1] = Math.max(interval[1], newInterval[1]);
            } else {
                res.add(interval);
            }
        }
        res.add(newInterval);
        Collections.sort(res, (o1, o2) -> o1[0] - o2[0]);
        return res.toArray(new int[res.size()][]);
    }
}

301. 删除无效的括号

看到最少数量,优先想到使用bfs求解。
先判断整个字符串是否合法, 如果合法的话就将其加入到结果中。否则的话,进行下一步。
只删掉 1 个括号,考虑所有的删除情况,然后判断剩下的字符串是否合法,如果合法的话就将其加入到结果中。否则的话,进行下一步。
只删掉 2 个括号,考虑所有的删除情况,然后判断剩下的字符串是否合法,如果合法的话就将其加入到结果中。否则的话,进行下一步...
因为我们考虑删除最少的括号数,如果上边某一步出现了合法情况,后边的步骤就不用进行了。
要解决重复的问题,除了解法一在最后返回前用 set 去重。这里我们也可以在过程中使用一个 set ,在加入队列之前判断一下是否重复。

class Solution {
    private boolean isValid(String s) {
        int count = 0;
        for (char c : s.toCharArray()) {
            if (c == '(') {
                count++;
            } else if (c == ')') {
                count--;
            }
            if (count < 0) {
                return false;
            }
        }
        return count == 0;
    }

    public List<String> removeInvalidParentheses(String s) {
        List<String> res = new ArrayList<>();
        Queue<String> q = new LinkedList<>();
        Set<String> set = new HashSet<>();
        q.offer(s);
        while (true) {
            int size = q.size();
            for (int i = 0; i < size; i++) {
                String front = q.poll();
                if (isValid(front)) {
                    res.add(front);
                } else if (res.size() == 0) {
                    for (int j = 0; j < front.length(); j++) {
                        if (s.charAt(j) == '(' || s.charAt(j) == ')') {
                            String sub = front.substring(0, j) + front.substring(j + 1);
                            if (!set.contains(sub)) {
                                q.offer(sub);
                                set.add(sub);
                            }
                        }
                    }
                }
            }
            if (res.size() > 0) {
                break;
            }
        }
        return res;
    }
}

推荐阅读更多精彩内容

  • 读完本文,你可以去力扣拿下如下题目: 111.二叉树的最小深度[https://leetcode-cn.com/p...
    labuladong阅读 210评论 0 5
  • 堆与优先队列 优先队列 之前我们讲过队列这种数据结构,队列的特点是先进先出,那什么是优先队列呢?一般来说,优先队列...
    YXCoder阅读 446评论 0 0
  • 排序算法分类 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线...
    C1R2阅读 246评论 0 7
  • 算法与实现 选择排序 算法思想 从数组中选择最小元素,将它与数组的第一个元素交换位置。再从数组剩下的元素中选择出最...
    Java斗帝之路阅读 1,128评论 4 24
  • (1)插入排序 【思路】:将队列分为有序区和无序区两部分,每次都从无序区选第一个插入到前面的有序区中的正确位置。插...
    Wood_易_J阅读 164评论 1 3