力扣Hot100(目前进度89/100)

HOT100

简单题

两数之和

HashMap

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

有效的括号

数组模拟栈

class Solution {
    public boolean isValid(String s) {
        int top = 1;
        char[] stack = new char[s.length() + 1];
        for (char c : s.toCharArray()){
            if (c == '(' || c == '[' || c == '{'){
                stack[top++] = c;
            } else if (c == ')' && stack[--top] != '('){
                return false;
            } else if (c == ']' && stack[--top] != '['){
                return false;
            } else if (c == '}' && stack[--top] != '{'){
                return false;
            }
        }

        return top == 1;
    }
}

合并两个有序链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode res = new ListNode(0);
        ListNode pre = res;
        while (l1 != null && l2 != null){
            if (l1.val <= l2.val){
                //不额外申请新的ListNode,在原有链表基础上进行操作
                ListNode p = l1;
                l1 = l1.next;
                p.next = null;
                pre.next = p;
            } else {
                ListNode q = l2;
                l2 = l2.next;
                q.next = null;
                pre.next = q;
            }
            pre = pre.next;
        }
        pre.next = l1 != null ? l1 : l2;
        return res.next;

    }
}

最大子序和

class Solution {
    // public int maxSubArray(int[] nums) {
    //     int n = nums.length;
    //     //dp[i]代表以nums[i]结尾的连续子数组的最大和
    //     int[] dp = new int[n];
    //     int maxSum = nums[0];
    //     dp[0] = nums[0];
    //     for (int i = 1; i < n; i++){
    //         dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
    //         maxSum = Math.max(maxSum, dp[i]);
    //     }
    //     return maxSum;
    // }

    //状态压缩
    public int maxSubArray(int[] nums) {
        int n = nums.length;
        int dp_0 = nums[0], dp_1 = 0;
        int maxSum = dp_0;
        for (int i = 1; i < n; i++){
            dp_1 = Math.max(dp_0 + nums[i], nums[i]);
            dp_0 = dp_1;
            maxSum = Math.max(maxSum, dp_1);
        }
        return maxSum;
    }
}

爬楼梯

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

    //状态压缩
    public int climbStairs(int n) {
        if (n <= 2) return n; 
        int dp_0 = 1, dp_1 = 1;
        for (int i = 2; i <= n; i++){
            int sum = dp_1 + dp_0;
            dp_0 = dp_1;
            dp_1 = sum;
        }
        return dp_1;
    }
}

对称二叉树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null) return true;
        return dfs(root.left, root.right);
    }

    public boolean dfs(TreeNode p, TreeNode q){
        //都为null,树仅有一个根节点
        if (p == null && q == null) return true;
        //仅有一个为空(由上面的情况排除都为空)
        if (p == null || q == null) return false;
        if (p.val != q.val) return false;
        return dfs(p.left, q.right) && dfs(p.right, q.left);
    }
}

二叉树的最大深度

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

买卖股票的最佳时期

class Solution {
    // public int maxProfit(int[] prices) {
    //     //dp[i][0]代表第i天手中未持股时的最大利润,dp[i][1]代表持股的
    //     int n = prices.length;
    //     int[][] dp = new int[n][2];
    //     dp[0][0] = 0;
    //     dp[0][1] = -prices[0];
    //     for (int i = 1; i < n; i++){
    //         dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
    //         //只交易一次,之前未持股,说明还未交易,所以dp[i - 1][0]必等于0
    //         dp[i][1] = Math.max(dp[i - 1][1], -prices[i]);
    //     }
    //     return dp[n - 1][0];
    // }

    //状态压缩
    public int maxProfit(int[] prices) {
        int n = prices.length;
        int dp_0 = 0, dp_1 = -prices[0];
        for (int i = 1; i < n; i++){
            dp_0 = Math.max(dp_0, dp_1 + prices[i]);
            dp_1 = Math.max(dp_1, -prices[i]);
        }
        return dp_0;
    }
}

只出现一次的数字

巧用异或运算 —— 所有出现次数为两次的元素做异或结果为0,和0异或结果为本身

class Solution {
    public int singleNumber(int[] nums) {
        int sum = 0;
        for (int num : nums){
            sum ^= num;
        }
        return sum;
    }
}

环形链表

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode fastPtr = head, slowPtr = head;
        while (fastPtr != null && fastPtr.next != null){
            fastPtr = fastPtr.next.next;
            slowPtr = slowPtr.next;
            if (fastPtr == slowPtr) return true;
        }
        return false;
    }
}

最小栈

class MinStack {

    Deque<Integer> stk1;
    Deque<Integer> stk2;
    /** initialize your data structure here. */
    public MinStack() {
        stk1 = new ArrayDeque<>();
        stk2 = new ArrayDeque<>();
        //防止stk2.peek()出现空指针异常
        stk2.push(Integer.MAX_VALUE);
    }
    
    public void push(int val) {
        stk1.push(val);
        stk2.push(Math.min(stk2.peek(), val));
    }
    
    public void pop() {
        if (!stk1.isEmpty() && !stk2.isEmpty()){
            stk1.pop();
            stk2.pop();
        } else {
            return;
        }

    }
    
    public int top() {
        return stk1.peek();
    }
    
    public int getMin() {
        return stk2.peek();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(val);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.getMin();
 */

相交链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode p = headA, q = headB;
        while (p != q){
            p = p == null ? headB : p.next;
            q = q == null ? headA : q.next;
        }
        return p;
    }
}

多数元素

投票法 —— 投票抵消

class Solution {
    public int majorityElement(int[] nums) {
        int candidate = nums[0];
        int vote = 1;
        for (int num : nums){
            if (num == candidate){
                vote++;
            } else if (--vote == 0) {
                candidate = num;
                vote = 1;
            }
        }
        return candidate;
    }
}

反转链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode res = new ListNode(0);
        res.next = head;
        ListNode pre = res, p = head;
        while (p != null && p.next != null){
            ListNode temp = pre.next;
            pre.next = p.next;
            p.next = p.next.next;
            pre.next.next = temp;
        }
        return res.next;
    }
}

翻转二叉树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root == null) return null;

        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;

        invertTree(root.left);
        invertTree(root.right);

        return root; 
    }
}

回文链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
        Deque<Integer> stk = new ArrayDeque<>();
        ListNode fastPtr = head, slowPtr = head;
        //快指针走到最后一个节点或者空节点时,慢指针正好走到链表的中间节点
        while (fastPtr != null && fastPtr.next != null){
            stk.push(slowPtr.val);
            fastPtr = fastPtr.next.next;
            slowPtr = slowPtr.next;
        }
        //奇长链表
        if (fastPtr != null){
            stk.push(slowPtr.val);
        }
        //此时slowptr指向链表的中间节点
        while (slowPtr != null && !stk.isEmpty()){
            if (slowPtr.val != stk.pop()) return false;
            slowPtr = slowPtr.next;
        }
        return true;
    }
}

移动零

class Solution {
    public void moveZeroes(int[] nums) {
        int n = nums.length;
        int nonZeroCnt = 0;
        for (int num : nums){
            if (num != 0){
                nums[nonZeroCnt++] = num;
            }
        }
        //零填充
        Arrays.fill(nums, nonZeroCnt, n, 0);
    }
}

找到所有数组中消失的数字

class Solution {
    public List<Integer> findDisappearedNumbers(int[] nums) {
        List<Integer> res = new ArrayList<>();
        int i = 0;
        while (i < nums.length){
            //当前数字应该在的位置上是该数字,则不交换
            int idx = nums[i];
            if (nums[i] == nums[idx - 1]) {
                i++;
                continue;
            }

            //若当前数字应该在的位置上不是该数字,则交换
            int temp = nums[i];
            nums[i] = nums[temp - 1];
            nums[temp - 1] = temp;
        }

        for (int k = 0; k < nums.length; k++){
            if (nums[k] != k + 1){
                res.add(k + 1);
            }
        }
        return res;
    }
}

汉明距离

class Solution {
    public int hammingDistance(int x, int y) {
        int distance = 0;
        //汉明距离==异或和中1的个数
        int sum = x ^ y;
        while (sum != 0){
            distance++;
            //消除最右边的1
            sum &= (sum - 1);
        }
        return distance;
    }
}

二叉树的直径

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    int res = 0;
    public int diameterOfBinaryTree(TreeNode root) {
        getHeight(root);
        return res;
    }

    public int getHeight(TreeNode node){
        if (node == null) return 0;
        int lHeight = getHeight(node.left);
        int rHeight = getHeight(node.right);
        //更新最大直径
        res = Math.max(res, lHeight + rHeight);
        //返回当前节点的最大高度
        return 1 + Math.max(lHeight, rHeight);
    }
}

合并二叉树

class Solution {
	public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
		// 如果root1和root2中,只要有一个是null,函数就直接返回
		if(root1 == null || root2 == null) {
			return root1 == null ? root2 : root1;
		}
		//让root1的值等于root1和root2的值累加,再递归地计算两颗树的左节点、右节点
        //把第二棵树直接加到第一棵树上
		root1.val += root2.val;
		root1.left = mergeTrees(root1.left, root2.left);
		root1.right = mergeTrees(root1.right, root2.right);
		return root1;
	}
}

中等题

两数相加

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode res = new ListNode(0);
        ListNode pre = res;
        int add = 0;
        while (l1 != null || l2 != null){
            int sum = add;
            if (l1 != null) {
                sum += l1.val;
                l1 = l1.next;
            }
            if (l2 != null) {
                sum += l2.val;
                l2 = l2.next;
            }
            
            pre.next = new ListNode(sum % 10);
            add = sum / 10;

            pre = pre.next;
        }
        if (add != 0){
            pre.next = new ListNode(add);
        }
        return res.next;
    }
}

无重复字符的最长子串

滑动窗口

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s.length() <= 1) return s.length(); 
        HashMap<Character, Integer> map = new HashMap<>();
        int left = 0, right = 0, maxLen = Integer.MIN_VALUE;
        while (right < s.length()){
            char c = s.charAt(right);
            map.put(c, map.getOrDefault(c, 0) + 1);
            //收缩窗口
            while (map.get(c) > 1){
                char d = s.charAt(left);
                left++;
                map.put(d, map.get(d) - 1);
            }
            //更新窗口最大长度
            maxLen = Math.max(maxLen, right - left + 1);
            right++;
        }
        return maxLen;
    }
}

最长回文子串

动态规划

class Solution {
    public String longestPalindrome(String s) {
        int n = s.length();
        if (n <= 1) return s;
        //dp[i][j]指示从chars[i]到chars[j]这段子串是否是回文的
        char[] chars = s.toCharArray();
        
        boolean [][] dp = new boolean[n][n];
        //记录最大回文子串的起止位置
        int begin = 0, end = 0;
        //动态规划
        for (int j = 0; j < n; j++){
            dp[j][j] = true;
            for (int i = 0; i < j; i++){
                //子串长为2
                if (i == j - 1){
                    //回文取决于这两个字符是否相等
                    dp[i][j] = chars[i] == chars[j];
                } else {
                    //回文取决于dp[i + 1][j - 1]以及最外侧两侧的字符是否相等
                    dp[i][j] = dp[i + 1][j - 1] && chars[i] == chars[j];
                }
                //更新最大回文子串的起止位置
                if (dp[i][j] && j - i > end - begin){
                    begin = i;
                    end = j;
                }
            }
        }
        return s.substring(begin, end + 1);
    }
}

盛最多水的容器

双指针

class Solution {
    public int maxArea(int[] height) {
        int n = height.length;
        int maxVolume = 0;
        int leftPtr = 0, rightPtr = n - 1;
        while (leftPtr < rightPtr){
            int h = Math.min(height[leftPtr], height[rightPtr]);
            maxVolume = Math.max(maxVolume, h * (rightPtr - leftPtr));
            
            //由于移动指针导致宽的减小,必须把指针移向更高处才能使容量增大
            while (height[leftPtr] <= h && leftPtr < rightPtr) leftPtr++;
            while (height[rightPtr] <= h && leftPtr < rightPtr) rightPtr--;
        }
        return maxVolume;
    }
}

三数之和

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        int n = nums.length;
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(nums);
        for (int k = 0; k < n - 2; k++){
            //nums[j] > nums[i] > nums[k] > 0, nums[k] + nums[i] + nums[j] = 0无解
            if (nums[k] > 0) break;
            //nums[k] == nums[k - 1](已经把nums[k - 1]的全部组合解考虑完了,因此可以跳过重复解)
            if (k > 0 && nums[k] == nums[k - 1]) {
                continue;
            }
            int i = k + 1, j = n - 1;
            while (i < j){
                int sum = nums[k] + nums[i] + nums[j];
                if (sum < 0) {
                    //指针i右移,增大sum进行搜索,对于相同的组合,跳过就行
                    while (i < j && nums[i] == nums[++i]);
                } else if(sum > 0){
                    //指针j左移,减小sum进行搜索,对于相同的组合,跳过就行
                    while (i < j && nums[j] == nums[--j]);
                } else {
                    res.add(new ArrayList<Integer>(Arrays.asList(nums[k], nums[i], nums[j])));
                    //还是要跳过重复的解
                    while (i < j && nums[i] == nums[++i]);
                    while (i < j && nums[j] == nums[--j]);
                }
            }
        }
        return res;
    }
}

电话号码的字母组合

class Solution {
    private static String[] map = new String[]{"abc", "def", "ghi", "jkl", "mno","pqrs", "tuv", "wxyz"};
    public List<String> letterCombinations(String digits) {
        List<String> res = new ArrayList<>();
        if (digits.length() == 0) return res;
        helper(res, digits, "");
        return res;
    }

    public void helper(List<String> res, String digits, String ans){
        if (ans.length() == digits.length()){
            res.add(ans);
            return;
        }
        for (char c : map[digits.charAt(ans.length()) - '2'].toCharArray()){
            helper(res, digits, ans + c);
        }
    }
}

删除链表的倒数第N个结点

双指针

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode res = new ListNode(0);
        res.next = head;
        ListNode p = res, q = res;
        while (q.next != null){
            //让q多走n步,等到q走到链表的末尾,此时p也走到了要删除节点的前一个节点处
            if (n > 0){
                q = q.next;
                n--;
                continue;
            }
            p = p.next;
            q = q.next;
        }
        //删除节点
        p.next = p.next.next;
        //返回头节点
        return res.next;
    }
}

括号生成

回溯

class Solution {
    private List<String> res = new ArrayList<>();
    public List<String> generateParenthesis(int n) {
        String cur = "";
        backTrack(cur, n, n);
        return res;
    }

    //left right,左右括号的剩余量
    public void backTrack(String s, int left, int right){
        //终止条件
        if (left == 0 && right == 0){
            res.add(s);
            return;
        }

        //做选择,还有左括号,就可以把左括号加到s中
        if (left > 0) {
            backTrack(s + "(", left - 1, right);
        }

        //当前右括号剩余个数多于左括号(右括号的添加必须在左括号之后),才可以把右括号加到s中
        if (right > left) {
            backTrack(s + ")", left, right - 1);
        }
    }
}

下一个排列

class Solution {
    public void nextPermutation(int[] nums) {
        int n = nums.length;
        int i = n - 2;
        //从后往前找第一个比其右邻居小的元素
        while (i >= 0 && nums[i] >= nums[i + 1]){
            i--;
        }

        //找到后,从最后一个元素开始,找第一个比nums[i]大的元素
        if (i >= 0){
            int j = n - 1;
            while (j >= 0 && nums[j] <= nums[i]){
                j--;
            }
            swap(nums, i, j);
        }
        //把nums[i]后面的元素升序排序,这样保证增幅最小,也即得到下一个更大的排列
        Arrays.sort(nums, i + 1, n);
    }

    public void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

搜索旋转排序数组

二分

class Solution {
    public int search(int[] nums, int target) {
        
        int n = nums.length;
        int left = 0, right = n - 1;
        while (left <= right){
            int mid = (right - left) / 2 + left;
            if (nums[mid] == target) return mid;
            //nums[mid]在左半部分
            if (nums[mid] >= nums[left]){
                //target的值也在左半部分之间
                if (nums[mid] > target && nums[left] <= target){
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            } else {
                if (nums[mid] < target && target <= nums[right]){
                    left = mid + 1;
                } else {
                    right = mid - 1;
                }

            }
        }
        return -1;
    }
}

在排序数组中查找元素的第一个和最后一个位置

二分 —— 边界搜索

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int res1 = getLeftBorder(nums, target);
        int res2 = getRightBorder(nums, target);

        return new int[]{res1, res2};
    }

    //搜索左侧边界
    public int getLeftBorder(int[] nums, int target){
        int n = nums.length;
        int left = 0, right = n - 1;
        while (left <= right){
            int mid = (left + right) / 2;
            if (nums[mid] >= target){
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        //不存在目标值的情况
        if (left >= n || nums[left] != target){
            return -1;
        }
        return left;
        
    }

    //搜索右侧边界
    public int getRightBorder(int[] nums, int target){
        int n = nums.length;
        int left = 0, right = n - 1;
        while (left <= right){
            int mid = (left + right) / 2;
            if (nums[mid] > target){
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        //不存在目标值的情况
        if (right < 0 || nums[right] != target){
            return -1;
        }
        return right;
    }
}

组合总和

回溯 —— 题目给的数组是无重复元素的,所以只要从前往后遍历就不会有重复解。

class Solution {
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        if (candidates == null || candidates.length == 0) return res;
        backTrack(candidates, new ArrayList<Integer>(), 0, target);
        return res;
    }

    public void backTrack(int[] nums, List<Integer> temp, int index, int k){
        //终止条件
        if (k < 0) return;
        if (k == 0){
            res.add(new ArrayList<>(temp));
            return;
        }
		
        //遍历选择列表,分层进行选择
        for (int i = index; i < nums.length; i++){
            //累积和还没达到target,才需要继续组合
            if (k > 0){
                //做选择
                temp.add(nums[i]);

                //回溯,i不自增的原因是,candidates中的元素可以无限制重复被选取
                backTrack(nums, temp, i, k - nums[i]);

                //撤销选择
                temp.remove(temp.size() - 1);
            }
            
        }
    }
}

全排列

常规解法

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        int[] visited = new int[nums.length];
        backtrack(res, nums, new ArrayList<Integer>(), visited);
        return res;

    }

    private void backtrack(List<List<Integer>> res, int[] nums, ArrayList<Integer> tmp, int[] visited) {
        if (tmp.size() == nums.length) {
            res.add(new ArrayList<>(tmp));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            if (visited[i] == 1) continue;
            visited[i] = 1;
            tmp.add(nums[i]);
            backtrack(res, nums, tmp, visited);
            visited[i] = 0;
            tmp.remove(tmp.size() - 1);
        }
    }
}

交换的思想

class Solution {
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> permuteList = new ArrayList<>();

    public List<List<Integer>> permute(int[] nums) {
        for (int i = 0; i < nums.length; i++){
            permuteList.add(nums[i]);
        }
        backTrack(0, nums.length);
        return res;
    }

    public void backTrack(int begin, int last){
        //终止条件
        if (begin == last){
            res.add(new ArrayList<>(permuteList));
        }
        for (int i = begin; i < last; i++){
            //做选择,即交换
            //Collections.swap(permuteList, begin, i);
            swap(begin, i);
            //继续回溯
            backTrack(begin + 1, last);
            //撤销选择,换回来
            //Collections.swap(permuteList, begin, i);
            swap(begin, i);
        }
    }

    //交换
    public void swap(int j, int k){
        int temp = permuteList.get(j);
        permuteList.set(j, permuteList.get(k));
        permuteList.set(k, temp);
    }
}

旋转图像

class Solution {
    public void rotate(int[][] matrix) {
        int m = matrix.length, n = matrix[0].length;
        //只需要一半进行顺时针轮转交换
        for (int i = 0; i < (n + 1) / 2; i++){
            for (int j = 0; j < n / 2; j++){
                // temp = 7
                int temp = matrix[n - 1 - j][i];
                // 9 -> 7(同一行)
                matrix[n - 1 - j][i] = matrix[n - 1 - i][n - 1 - j];
                // 3 -> 9(同一列))
                matrix[n - 1 - i][n - 1 - j] = matrix[j][n - 1 - i];
                // 1 -> 3(同一行)
                matrix[j][n - 1 - i] = matrix[i][j];
                // temp = 7 -> 1
                matrix[i][j] = temp;
            }
        }

    }
}

字母异位词分组

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        HashMap<String, List> map = new HashMap<>();
        for (String s : strs){
            char[] chars = s.toCharArray();
            Arrays.sort(chars);
            String sorted = String.valueOf(chars);
            if (!map.containsKey(sorted)){
                map.put(sorted, new ArrayList<String>());
            }
            map.get(sorted).add(s);
        }
        return new ArrayList(map.values());
    }
}

跳跃游戏

贪心

class Solution {
    public boolean canJump(int[] nums) {
        int farthest = nums[0];
        for (int i = 0; i <= farthest; i++){
            //更新最远能到达的位置
            farthest = Math.max(i + nums[i], farthest);
            if (farthest >= nums.length - 1) return true;
        }
        return false;
    }
}

合并区间

class Solution {
    public int[][] merge(int[][] intervals) {
        int n = intervals.length;
        //只有一个区间
        if (n == 1) return intervals;
        //将区间排序
        Arrays.sort(intervals, (a, b) -> {
            //区间起点相同,按区间长度由大到小排序
            if (a[0] == b[0]){
                return b[1] - a[1];
            }
            //按起始位置从小到大排序
            return a[0] - b[0];
        });
        int cnt = 0;
        int[][] res = new int[n][2];
        int start = intervals[0][0], end = intervals[0][1];
        for (int i = 1; i < n; i++){
            //有区间交叉,合并它们
            if (end >= intervals[i][0] && end <= intervals[i][1]){
                end = intervals[i][1];
            }
            //存区间
            res[cnt][0] = start;
            res[cnt][1] = end;
            //没有区间交叉
            if (end < intervals[i][0]){
                //产生新区间
                cnt++;
                //新区间的起止位置
                start = intervals[i][0];
                end = intervals[i][1];
                //对最后一个区间还未做处理
                if (end == intervals[n - 1][1]){
                    res[cnt][0] = start;
                    res[cnt][1] = end;
                }
            }
        }
        //返回数组的前cnt个处理好的合并区间
        return Arrays.copyOf(res, cnt + 1);
    }
}

不同路径

动态规划

class Solution {
    public int uniquePaths(int m, int n) {
        //dp[i][j]代表从(0,0)到(i,j)的方法数
        int[][] dp = new int[m][n];
        //base case
        for (int i = 0; i < m; i++){
            dp[i][0] = 1;
        }
        for (int j = 0; j < n; j++){
            dp[0][j] = 1;
        }
        for (int i = 1; i < m; i++){
            for (int j = 1; j < n; j++){
                //位置(i, j)只能由上方位置(i - 1, j)和左方位置(i, j - 1)到达
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }

        return dp[m - 1][n - 1];
    }
}

最小路径和

动态规划

class Solution {
    public int minPathSum(int[][] grid) {
        //dp[i][j]代表(0, 0)到(i, j)的最短路径
        int m = grid.length, n = grid[0].length;
        int[][] dp = new int[m][n];
        dp[0][0] = grid[0][0];
        for (int i = 1; i < m; i++){
            dp[i][0] = dp[i - 1][0] + grid[i][0];
        }
        for (int j = 1; j < n; j++){
            dp[0][j] = dp[0][j - 1] + grid[0][j];
        }

        for (int i = 1; i < m; i++){
            for (int j = 1; j < n; j++){
                dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
            }
        }

        return dp[m - 1][n - 1];
    }
}

颜色分类

要求原地方法——空O(1)

class Solution {
    public void sortColors(int[] nums) {
        int n0 = 0, n1 = 0;
        for (int i = 0; i < nums.length; i++){
            //暂存原位置的值
            int num = nums[i];
            //第一遍刷漆,刷上2
            nums[i] = 2;
            //第二遍刷漆,把2覆盖
            if (num < 2) {
                nums[n1++] = 1;
            }
            //第三遍刷漆,把1覆盖,上一次刷漆相当于只进行了n1++
            if (num < 1) {
                nums[n0++] = 0;
            }
        }
        
    }
}

子集

回溯

class Solution {
    private List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> subsets(int[] nums) {
        int n = nums.length;
        //
        for (int k = 0; k < n + 1; k++){
            backTrack(new ArrayList<Integer>(), nums, 0, k);
        }
        return res;
    }

    //回溯
    public void backTrack(List<Integer> cur, int[] arr, int idx, int len){
        //终止条件
        if (cur.size() == len){
            res.add(new ArrayList<Integer>(cur));
            return;
        }

        for (int i = idx; i < arr.length; i++){
            //做选择
            cur.add(arr[i]);
            //下一层
            backTrack(cur, arr, i + 1, len);
            //撤销选择
            cur.remove(cur.size() - 1);
        }

    }
}

单词搜索


二叉树的中序遍历

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    private List<Integer> res = new ArrayList<>();
    public List<Integer> inorderTraversal(TreeNode root) {
        inorder(root);
        return res;
        
    }

    public void inorder(TreeNode node){
        if (node == null) return;
        inorder(node.left);
        res.add(node.val);
        inorder(node.right);
    }
}

不同的二叉搜索树

动态规划

class Solution {
    public int numTrees(int n) {
        if (n <= 2) return n;
        //dp[i]代表节点个数为i的二叉搜索树的种数
        int[] dp = new int[n + 1];
        //base case
        dp[0] = 1;
        dp[1] = 1;
        dp[2] = 2;
        /**
        根结点不同,可能有n种情况,以1为根结点,以2为根结点…类推到以n为根结点,共有n种情况.
        区分了根节点不同后,剩下的就是左右子树不同了,而左右子树的不同二叉树则是一个相同的问题
        比如收当前有n个结点,以第k个元素作为根结点,那么左子树就会有k-1个元素,它是一个规模为k-1的子问题
        而右子树有n-k个结点,它也是一个规模为n-k的子问题
        */
        for (int i = 3; i <= n; i++){
            for (int j = 1; j <= i; j++) {
                dp[i] += dp[j - 1] * dp[i - j];
            }
        }
        return dp[n];
    }
}

验证二叉搜索树

中序遍历

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    private long pre = Long.MIN_VALUE;
    public boolean isValidBST(TreeNode root) {
        if (root == null) return true;
        //左子树不满足二叉搜索树的性质
        if (!isValidBST(root.left)){
            return false;
        }
        //如果当前节点值小于等于中序遍历的前一个节点值,说明不满足BST,返回 false;否则继续遍历。
        if (root.val <= pre){
            return false;
        }
        //更新pre
        pre = root.val;

        //再检查右子树是否满足二叉搜索树的性质
        return isValidBST(root.right);

    }
}

二叉树的层序遍历

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if (root == null) return res;
        Deque<TreeNode> q = new ArrayDeque<>();
        q.offer(root);
        while (!q.isEmpty()){
            int size = q.size();
            List<Integer> temp = new ArrayList<>();
            for (int i = 0; i < size; i++){
                TreeNode cur = q.poll();
                temp.add(cur.val);
                if (cur.left != null) q.offer(cur.left);
                if (cur.right != null) q.offer(cur.right);
            }
            res.add(temp);
        }
        return res;
    }
}

从前序与中序遍历序列构造二叉树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return helper(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
    }


    public TreeNode helper(int[] preorder, int preBegin, int preEnd, int[] inorder, int inBegin, int inEnd){
        if (preBegin > preEnd) return null;
        //前序序列用作确定根节点
        int rootVal = preorder[preBegin];
        //确定当前根节点在中序序列中的下标,用于分隔左右子树
        int idx = 0;
        for (int i = 0; i < inorder.length; i++){
            if (rootVal == inorder[i]){
                idx = i;
                break;
            }
        }
        //创建root
        TreeNode root = new TreeNode(rootVal);
        //中序遍历序列用于确定左右子树节点个数
        //左子树节点个数
        int leftSize = idx - inBegin;
        //递归创建左右子树
        root.left = helper(preorder, preBegin + 1, preBegin + leftSize, inorder, inBegin, idx - 1);
        root.right = helper(preorder, preBegin + leftSize + 1, preEnd, inorder, idx + 1, inEnd);
        return root;
    }
}

二叉树展开为链表

链表需要满足先序顺序 + left置空 + right作链接指针

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public void flatten(TreeNode root) {
        if (root == null) return;

        //先递归把左右子树拉平
        flatten(root.left);
        flatten(root.right);

        //暂存左右子树的根节点
        TreeNode left = root.left;
        TreeNode right = root.right;

        //将root的左指针置空
        root.left = null;
        //root右指针指向原来的左孩子
        root.right = left;

        //迭代到叶子节点
        TreeNode p = root;
        while (p.right != null){
            p = p.right;
        }
        //把右子树接上
        p.right = right;

    }
}

单词拆分


环形链表Ⅱ

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fastPtr = head, slowPtr = head;
        while (fastPtr != null && fastPtr.next != null){
            fastPtr = fastPtr.next.next;
            slowPtr = slowPtr.next;
            //快慢指针第一次相遇
            if (fastPtr == slowPtr) break;
        }

        //无环的情况
        if (fastPtr == null || fastPtr.next == null){
            return null;
        }

        slowPtr = head;
        while (slowPtr != fastPtr){
            fastPtr = fastPtr.next;
            slowPtr = slowPtr.next;
        }
        return slowPtr;
    }
}

LRU缓存机制

class LRUCache {
    private int capacity = 0;
    private Map<Integer, Integer> map = new LinkedHashMap<>();
    public LRUCache(int capacity) {
        this.capacity = capacity;
    }
    
    public int get(int key) {
        if (map.containsKey(key)){
            int val = map.get(key);
            //先移除再添加,保证每次查询后的key都在双向链表的末尾
            map.remove(key);
            map.put(key, val);
            return val;
        } else {
            return -1;
        }
    }
    
    //put操作也会访问key,所以也要先移除再更新
    public void put(int key, int value) {
        if (map.containsKey(key)) {
			map.remove(key);
		} else if (map.size() == capacity) {
            //移除链表头节点,即最近最久未使用的节点
			int oldestKey = map.keySet().iterator().next();
            map.remove(oldestKey);
		}
        //更新map中键key对应的value
		map.put(key, value);
    }
    
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

排序链表

归并排序

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    //归并排序O(NlogN)
    public ListNode sortList(ListNode head) {
        //归并排序
        if (head == null || head.next == null) return head;
        //快慢指针找中间节点
        ListNode slowPtr = head, fastPtr = head.next;
        while (fastPtr != null && fastPtr.next != null){
            fastPtr = fastPtr.next.next;
            slowPtr = slowPtr.next;
        }
        //记录后半部分链表的表头指针
        ListNode lastHalf = slowPtr.next;
        //将slowPtr所在位置后面的链表全部断开
        slowPtr.next = null;

        //递归
        ListNode leftHalf = sortList(head);
        ListNode rightHalf = sortList(lastHalf);
        return merge(leftHalf, rightHalf);
    }

    public ListNode merge(ListNode p, ListNode q){
        ListNode res = new ListNode(0);
        ListNode pre = res;
        while (p != null && q != null){
            if (p.val <= q.val){
                pre.next = p;
                p = p.next;
            } else {
                pre.next = q;
                q = q.next;
            }
            pre = pre.next;
        }
        pre.next = p != null ? p : q;

        return res.next;
    }
}

乘积最大子数组

动态规划

class Solution {
    public int maxProduct(int[] nums) {
        int n = nums.length;
        int maxRes = Integer.MIN_VALUE;
        int imax = 1, imin = 1;
        for (int i = 0; i < n; i++){
            // nums[i] < 0,交换imax和imin
            if (nums[i] < 0){
                int temp = imax;
                imax = imin;
                imin = temp;
            }
            // 维护的最小值乘完nums[i]变成最大值,而最大值乘完nums[i]变成最小值
            imax = Math.max(imax * nums[i], nums[i]);
            imin = Math.min(imin * nums[i], nums[i]);

            maxRes = Math.max(maxRes, imax);

        }
        return maxRes;

    }
}

打家劫舍

动态规划

class Solution {
    public int rob(int[] nums) {
        // dp[i]表示从第i家开始偷窃能获得的最高金额
        int n = nums.length;
        int[] dp = new int[n + 2];
        // 由后往前遍历
        for (int i = n - 1; i >= 0; i--){
            dp[i] = Math.max(dp[i + 1], dp[i + 2] + nums[i]);
        }
         return dp[0];
    }
}

岛屿数目

回溯

class Solution {
    public int numIslands(char[][] grid) {
        int m = grid.length, n = grid[0].length;
        int islands = 0;
        for (int i = 0; i < m; i++){
            for (int j = 0; j < n; j++){
                //陆地
                if (grid[i][j] == '1'){
                    islands++;
                    //深度优先搜索,把和(i, j)所在岛相连的陆地的都变为0
                    dfs(grid, i , j);
                }
                
            }
        }
        return islands;
    }

    public void dfs(char[][] arr, int i, int j){
        //停止搜索的条件
        if (arr[i][j] == '0') return;
        
        //置“0”操作
        arr[i][j] = '0';

        //向上搜索
        if (i - 1 >= 0) dfs(arr, i - 1, j);

        //向下搜索
        if (i + 1 <= arr.length - 1) dfs(arr, i + 1, j);

        //向左搜索
        if (j - 1 >= 0) dfs(arr, i, j - 1);

        //向右搜索
        if (j + 1 <= arr[0].length - 1) dfs(arr, i, j + 1);
    }
}

课程表


前缀树


数组中第K个最大元素

优先权队列

class Solution {
    int res = 0;
    public int findKthLargest(int[] nums, int k) {
        int n = nums.length;
        if (k > n / 2){
            PriorityQueue<Integer> maxHeap = new PriorityQueue<>(n, (a, b) -> b - a);
            getRes(maxHeap, nums, k);

        } else {
            PriorityQueue<Integer> minHeap = new PriorityQueue<>(n);
            getRes(minHeap, nums, n - k + 1);
        }
        return res;
    }

    //从最大堆或者最小堆中获取结果
    public void getRes(PriorityQueue<Integer> heap, int[] nums, int cnt){
        for (int num : nums){
            heap.offer(num);
        }
        while (cnt-- > 0){
            res = heap.poll();
        }
    }
}

最大正方形

image-20210511165453576

class Solution {
    public int maximalSquare(char[][] matrix) {
        int m = matrix.length, n = matrix[0].length;
        // dp[i + 1][j + 1]代表以(i, j)为右下角元素的方阵的最大维度
        int maxD = Integer.MIN_VALUE;
        int[][] dp = new int[m + 1][n + 1];
        for (int i = 1; i <= m ; i++){
            for (int j = 1; j <= n; j++){
                if (matrix[i - 1][j - 1] == '1'){
                    //状态转移,维度受限于左边、左对角线、正上方的维度(取三者中最小再加1)
                    dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i - 1][j], dp[i][j - 1])) + 1;
                }
                maxD = Math.max(maxD, dp[i][j]);
            }
        }
        return maxD * maxD;
    }
}

二叉树的最近公共祖先

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || root == p || root == q) return root;

        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);

        if (left != null && right != null) return root;

        return left != null ? left : right;
    }
}

除自身以外数组的乘积

前缀数组/后缀数组

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        int[] preProduct = new int[n];
        int[] postProduct = new int[n];
        preProduct[0] = 1;
        postProduct[n - 1] = 1;
        //前面的积
        for (int i = 1; i < n; i++){
            preProduct[i] = preProduct[i - 1] * nums[i - 1];
        }
        //后面的积
        for (int j = n - 2; j >= 0; j--){
            postProduct[j] = postProduct[j + 1] * nums[j + 1];
        }

        for (int k = 0; k < n; k++){
            nums[k] = preProduct[k] * postProduct[k];
        }
        return nums;
    }
}

搜索二维矩阵

双指针

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length, n = matrix[0].length;
        int left = 0, right = n - 1;
        while (left <= m - 1 && right >= 0){
            if (matrix[left][right] == target){
                return true;
            } else if (matrix[left][right] < target){
                left++;
            } else {
                right--;
            }
        }
        return false;
    }
}

完全平方数

class Solution {
    public int numSquares(int n) {
        // dp[i] —— 和为i的完全平方数的最少数目
        // dp[i] = Math.min(dp[i - 1], dp[i - 4], ...,) + 1
        int[] dp = new int[n + 1];
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[0] = 0;
        dp[1] = 1;
        for (int i = 2; i <= n; i++){
            int ps = 1;
            while (ps * ps <= i){
                dp[i] = Math.min(dp[i], dp[i - ps * ps]);
                ps++;
            }
            // 加1
            dp[i]++;
        }
        return dp[n];
    }
}

寻找重复数

二分

class Solution {
    public int findDuplicate(int[] nums) {
        // //HashSet
        // int n = nums.length;
        // HashSet<Integer> set = new HashSet<>();
        // for (int num : nums){
        //     if (set.contains(num)){
        //         return num;
        //     } else {
        //         set.add(num);
        //     }
        // }
        // return -1;
		
        //二分法
        int n = nums.length;
        int left = 1, right = n;
        while (left < right){
            int mid = (right - left) / 2 + left;
            int cnt = 0;
            // 统计小于mid的数有多少个
            for (int num : nums){
                if (num <= mid){
                    cnt++;
                }
            }
            //小于等于mid的数的个数大于mid,说明重复数在[left,mid]之间
            if (cnt > mid){
                right = mid;
            } else {
                left = mid + 1;
            } 
        }
        return left;
        
    }

}

最长递增子序列

动态规划

class Solution {
    public int lengthOfLIS(int[] nums) {
        // dp[i] —— 以nums[i]结尾的严格递增子序列的最大长度
        int n = nums.length;
        int[] dp = new int[n];
        Arrays.fill(dp, 1);
        int maxLen = Integer.MIN_VALUE;

        for (int i = 0; i < n; i++){
            for (int j = 0; j < i; j++){
                if (nums[j] < nums[i]){
                    // 状态更新
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            maxLen = Math.max(maxLen, dp[i]);
        }
        return maxLen;
    }
}

最佳买卖股票时机含冷冻期

动态规划

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        if (n == 0) return 0;
        
        //base case
        int dp_0 = 0, dp_1 = Integer.MIN_VALUE;
        int dp_2 = 0;
        //state update
        for (int i = 0; i < n; i++){
            int temp = dp_0;
            dp_0 = Math.max(dp_0, dp_1 + prices[i]);
            // 昨天就有或者是前天买入的
            dp_1 = Math.max(dp_1, dp_2 - prices[i]);
            dp_2 = temp;
        }
        return dp_0;
    }
}

零钱兑换

动态规划

class Solution {
    public int coinChange(int[] coins, int amount) {
        // dp[amount]代表凑齐总金额amount需要的最小硬币个数
        int[] dp = new int[amount + 1];

        Arrays.fill(dp, amount + 1);

        // base case
        dp[0] = 0;

        for (int i = 0; i <= amount; i++){
            for (int coin : coins){
                // sub problem has no solution
                if (i - coin < 0) continue;
                //state update
                dp[i] = Math.min(dp[i], 1 + dp[i - coin]);
            }
        }
		// 凑齐金额amount至多需要amount枚硬币
        return dp[amount] == amount + 1 ? -1 : dp[amount];
        

    }
}

打家劫舍Ⅲ

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int rob(TreeNode root) {
        int[] res = dfs(root);
        return res[0] > res[1] ? res[0] : res[1];
    }

    public int[] dfs(TreeNode node){
        if (node == null) {
            return new int[]{0, 0};
        }
        int[] left = dfs(node.left);
        int[] right = dfs(node.right);

        //当前节点抢,则左右孩子节点肯定不能抢
        int rob = node.val + left[1] + right[1];
        // 当前节点不抢,则左右孩子节点看情况(按收益最大)选择抢或不抢
        int noRob = (left[0] > left[1] ? left[0] : left[1])
                   +(right[0] > right[1] ? right[0] : right[1]);

        return new int[]{rob, noRob};

    }
}

比特位计数

位运算 —— 与&

class Solution {
    public int[] countBits(int num) {
        int[] res = new int[num + 1];
        for (int i = 0; i <= num; i++){
            res[i] = getOneCnt(i);
        }
        return res;
    }
    public int getOneCnt(int n){
        int cnt = 0;
        while (n != 0){
            n &= (n - 1);
            cnt++;
        }
        return cnt;
    }
}

前K个高频元素

HashMap + PriorityQueue

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        int[] res = new int[k];
        Map<Integer, Integer> map = new HashMap<>();
        for (int num : nums){
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        PriorityQueue<Map.Entry<Integer, Integer>> pq = new PriorityQueue<>((a, b) -> (b.getValue() - a.getValue()));
        map.entrySet().forEach((entry) -> pq.add(entry));
        int temp = k;
        while (k-- > 0 && !pq.isEmpty()){
            res[temp - k - 1] = pq.poll().getKey();
        }
        return res;
        
    }
}

字符串解码

辅助栈

class Solution {
    public String decodeString(String s) {
        int n = s.length();
        Deque<Integer> stkDigit = new ArrayDeque<>();
        Deque<String> stkStr = new ArrayDeque<>();

        StringBuilder res = new StringBuilder();
        int repeatedTimes = 0;
        for (char c : s.toCharArray()){
            if (Character.isDigit(c)){
                repeatedTimes = repeatedTimes * 10 + Integer.parseInt(c + "");
            } else if (c == '['){
                stkDigit.push(repeatedTimes);
                stkStr.push(res.toString());
                repeatedTimes = 0;
                res = new StringBuilder();
            } else if (c == ']'){
                StringBuilder temp = new StringBuilder();
                int times = stkDigit.pop();
                while(times-- > 0) temp.append(res);
                res = new StringBuilder(stkStr.pop() + temp);
            } else {
                res.append(c);
            }
        }
        return res.toString();
    }
}

除法求值


根据身高重建队列

贪心 —— 个子低的对个子高的之间的相对站位没有影响,可以先按身高降序排好(相同身高按k升序排列),再从前往后把身高为hk的人插到队列的第k个位置上

class Solution {
    public int[][] reconstructQueue(int[][] people) {
        int m = people.length;
        int[][] res = new int[m][2];
        Arrays.sort(people, (o1, o2) -> o1[0] == o2[0] ? o1[1] - o2[1] : o2[0] - o1[0]);
        LinkedList<int[]> list = new LinkedList<>();
        for(int[] pp : people){
            list.add(pp[1], pp);
        }
        return list.toArray(new int[m][2]);

    }
}

分割等和子集

动态规划

class Solution {
    public boolean canPartition(int[] nums) {
        int sum = Arrays.stream(nums).sum();

        if (sum % 2 != 0) return false;
        
        int target = sum / 2;
        
        //dp[i]代表和为i的子集是否存在
        boolean[] dp = new boolean[target + 1];
        Arrays.fill(dp, false);

        dp[0] = true;
        for (int i = 0; i < nums.length; i++){
            for (int j = target; j >=0; j--){
                if (j >= nums[i]){
                    dp[j] = dp[j] || dp[j - nums[i]];
                }
            }
        }
        return dp[target];
    }
}

路径总和Ⅲ

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int pathSum(TreeNode root, int targetSum) {
        if (root == null) return 0;
        // 递归,对树中每个节点都做dfs(只能由父到子)
        return dfs(root, targetSum) + pathSum(root.left, targetSum) + pathSum(root.right, targetSum);
    }

    public int dfs(TreeNode cur, int target) {
        int cnt = 0;
        if (cur == null) return 0;
        //统计符合路径和的路径条数
        if (target == cur.val) {
            cnt++;
        }
        cnt += dfs(cur.left, target - cur.val);
        cnt += dfs(cur.right, target - cur.val);
        return cnt;
    }
}

找到字符串中所有字母异位词

滑动窗口

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> res = new ArrayList<>();
        if (s.length() < p.length()) return res;
        int[] counts = new int[26];
        int[] window = new int[26];
        // 统计字符串p中所有字母出现的次数
        for (char c : p.toCharArray()){
            counts[c - 'a']++;
        }
        int left = 0, right = 0, cnt = 0;
        while (right < s.length()){
            int curRight = s.charAt(right) - 'a';
            window[curRight]++;
            while (window[curRight] > counts[curRight]){
                int curLeft = s.charAt(left) - 'a';
                window[curLeft]--;
                left++;
            }
            if (right - left + 1 == p.length()){
                res.add(left);
            }
            right++;

        }
        return res;
    }
}

目标和

回溯

class Solution {
    int cnt = 0;
    public int findTargetSumWays(int[] nums, int target) {
        backTrack(nums, 0, target);
        return cnt;
    }

    public void backTrack(int[] arr, int idx, int k){
        // 终止条件
        if (idx == arr.length){
            if (k == 0){
                cnt++;
            }
            return;
        }

        // 减去当前元素
        k -= arr[idx];
        backTrack(arr, idx + 1, k);
        // 加回来(撤销选择)
        k += arr[idx];

        // 加上当前元素
        k += arr[idx];
        backTrack(arr, idx + 1, k);
        k -= arr[idx];

    }
}

转化为背包问题

class Solution {
/*
假设nums中,正数的和为x,负数的和为y,则S=x-y,sum(nums) = x + y,由此推出x = (S + sum) / 2,问题转化为在nums中寻找若干数使得和为(S+sum)/2,这时问题转化为了01背包问题。
dp[j] —— 填满容量为j的背包,有dp[j]种方法。
状态转移 —— dp[j] = dp[j] + dp[j - num]; 当前填满容量为j的包的方法数 = 之前填满容量为j的包的方法数 + 之前填满容量为j - num的包的方法数。也就是当前数num的加入,可以把之前和为j - num的方法数加入进来。
*/
    public int findTargetSumWays(int[] nums, int S) {
        int sum = 0;
        for (int num : nums) sum += num;
        //不可能有解的情况
        //1. 数组中全部数字(正)的和小于S
        //2. (sum + S) / 2 为奇数,此时x不是整数
        if (sum < S || (sum + S) % 2 == 1){
            return 0;
        }
        return subset(nums, (sum + S) / 2);
        
    }
    
    public int subset(int[] arr, int target){
        int[] dp = new int[target + 1];
        dp[0] = 1;
        for (int i = 1; i <= arr.length; i++){
            for (int j = target; j >= 0; j--){
                if (j >= arr[i - 1]){
                    dp[j] = dp[j] + dp[j - arr[i - 1]];
                } else {
                    dp[j] = dp[j];
                }
            }
        }
        return dp[target];
    }
}

将二叉搜索树转化为累加树

改造中序遍历

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    int newVal = 0;
    public TreeNode convertBST(TreeNode root) {
        return modifiedInorder(root);
    }

    public TreeNode modifiedInorder(TreeNode node){
        if (node == null) return null;
        modifiedInorder(node.right);
        newVal += node.val;
        node.val = newVal;
        modifiedInorder(node.left);
        return node;
    }
}

和为K的子数组

前缀和+HashMap

class Solution {
    public int subarraySum(int[] nums, int k) {
        HashMap<Integer, Integer> map = new HashMap<>();
        map.put(0, 1);
        int sum = 0, cnt = 0;
        for (int num : nums){
            sum += num;
            if (map.containsKey(sum - k)){
                cnt += map.get(sum - k);
            } 
            map.put(sum, map.getOrDefault(sum, 0) + 1);
        }
        return cnt;
    }
}

最短无序连续子数组

class Solution {
    public int findUnsortedSubarray(int[] nums) {
        int n = nums.length;
        int[] ori = nums.clone();
        int left = Integer.MAX_VALUE;
        int right = Integer.MIN_VALUE;
        Arrays.sort(nums);
        for (int i = 0; i < n; i++){
            if (nums[i] != ori[i]){
                left = Math.min(left, i);
                right = Math.max(right, i);
            }
        }
        return right > left ? right - left + 1 : 0;

    }
}

任务调度器


回文子串

class Solution {
    public int countSubstrings(String s) {
        // dp[j][i]——子串s.substring(j, i + 1)的回文性
        int n = s.length();
        int cnt = 0;
        boolean[][] dp = new boolean[n][n];

        for (int i = 0; i < n; i++){
            for (int j = 0; j <= i; j++){
                // 长度为1
                if (i == j) {
                    dp[j][i] = true;
                } else if (j == i - 1){
                    // 长度为2
                    dp[j][i] = s.charAt(j) == s.charAt(i);
                } else {
                    dp[j][i] = dp[j + 1][i - 1] && s.charAt(j) == s.charAt(i);
                }
                if (dp[j][i] == true) cnt++;
            }
        }
        return cnt;
    }
}

每日温度

单调栈

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int n = temperatures.length;
        Deque<Integer> stk = new ArrayDeque<>();
        int[] res = new int[n];
        
        for (int i = 0; i < n; i++){
            // 维护一个单调递减的栈
            while (!stk.isEmpty() && temperatures[i] > temperatures[stk.peek()]){
                int pre = stk.pop();
                res[pre] = i - pre;
            }
            stk.push(i);
        }
        return res;
    }
}

困难题

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

优先权队列

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int tLen = nums1.length + nums2.length;
        PriorityQueue<Integer> pq = new PriorityQueue<>(tLen);
        int cnt = 0;
        for (int num : nums1){
            pq.offer(num);
        }
        for (int num : nums2){
            pq.offer(num);
        }

        int k = 0;
        while (k < tLen){
            double tmp = pq.poll() * 1.0;
            //长度为奇
            if (tLen % 2 != 0){
                if (k == tLen / 2) return tmp;
            } else {
                if (k == tLen / 2 - 1) return (tmp + pq.poll() * 1.0) / 2;
            }
            k++;
        }
        return -1.0;
    }
}

正则表达式匹配

递归

class Solution {
    public boolean isMatch(String s, String p) {
        if (p.equals("")) return s.equals("");
        // 当前位置是否匹配
        boolean firstMatch = !s.equals("") && (p.charAt(0) == s.charAt(0) || p.charAt(0) == '.');
        // 处理"*"的匹配,匹配0次,则跳过该字符和"*",匹配1次,往后移动s接着匹配,
        if (p.length() >= 2 && p.charAt(1) == '*') {
            return isMatch(s, p.substring(2)) || firstMatch && isMatch(s.substring(1), p);
        } else {
            return firstMatch && isMatch(s.substring(1), p.substring(1));
        }
    }
}

合并K个升序链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists == null || lists.length == 0) return null;
        return merge(lists, 0, lists.length - 1);
    }

    // 分治
    public ListNode merge(ListNode[] lists, int left, int right) {
        if (left > right) return null;
        if (left == right) return lists[left];
        int mid = (right - left) / 2 + left;
        ListNode l1 = merge(lists, left, mid);
        ListNode l2 = merge(lists, mid + 1, right);
        return mergeTowList(l1, l2);
    }

    // 合并两个链表
    public ListNode mergeTowList(ListNode l1, ListNode l2) {
        if (l1 == null || l2 == null) {
            return l1 != null ? l1 : l2;
        }

        ListNode res = new ListNode(0);
        ListNode temp = res;
        while (l1 != null && l2 != null) {
            if (l1.val <= l2.val) {
                temp.next = l1;
                l1 = l1.next;
            } else {
                temp.next = l2;
                l2 = l2.next;
            }
            temp = temp.next;
        }
        temp.next = l1 != null ? l1 : l2;

        return res.next;
    }
}

最长有效括号


接雨水

O(n), O(n)

class Solution {
    public int trap(int[] height) {
        int n = height.length;
        int[] maxLeft = new int[n];
        int[] maxRight = new int[n];
        int res = 0;
        // 左侧的最大高度
        for (int i = 1; i < n; i++){
            maxLeft[i] = Math.max(maxLeft[i - 1], height[i - 1]);
        }
        // 右侧的最大高度
        for (int i = n - 2; i >= 1; i--){
            maxRight[i] = Math.max(maxRight[i + 1], height[i + 1]);
        }

        // 最左(0)最右(n - 1)的位置都存不了水
        for (int i = 1; i < n - 1; i++){
            int minHeight = Math.min(maxLeft[i], maxRight[i]);
            if (height[i] < minHeight) {
                res += (minHeight - height[i]);
            }
        }
        return res;
        
    }
}

O(n), O(1)

class Solution {
    public int trap(int[] height) {
        int left = 0, right = height.length - 1;
        int leftMax = 0, rightMax = 0;
        int res = 0;
        while (left < right) {
            // 左右指针指向的柱子中更低的柱子
            if (height[left] < height[right]) {
                // 存不了水,更新左侧最大高度即可
                if (height[left] >= leftMax) {
                    leftMax = height[left];
                } else {
                    res += (leftMax - height[left]);
                }
                left++;
            } else {
                // 存不了水,更新右侧最大高度
                if (height[right] >= rightMax) {
                    rightMax = height[right];
                } else {
                     res += (rightMax - height[right]);
                }
                right--;
            }
        }
        return res;
        
    }
}

编辑距离

image-20210524150442708

动态规划 —— dp[i - 1][j - 1]代表s1[0,...,i]到s2[0,...,j]的最小编辑距离

状态转换:

  • word1.charAt(i - 1) == word2.charAt(j - 1) —> 跳过
  • 否则,从增删替中选需要最小操作的,然后操作数+1
    • dp[i - 1][j] —— 删
    • dp[i][j - 1] —— 增
    • dp[i - 1][j - 1] —— 替
class Solution {
    public int minDistance(String word1, String word2) {
        int n1 = word1.length(), n2 = word2.length();
        int[][] dp = new int[n1 + 1][n2 + 1];
        // base case
        for (int i = 1; i <= n1; i++){
            dp[i][0] = i;
        }
        for (int i = 1; i <= n2; i++){
            dp[0][i] = i;
        }

        for (int i = 1; i <= n1; i++) {
            for (int j = 1; j <= n2; j++) {
                // update state
                if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    dp[i][j] = minHelper(dp[i - 1][j] + 1, dp[i - 1][j - 1] + 1, dp[i][j - 1] + 1);
                }
            }
        }
        return dp[n1][n2];
    }

    public int minHelper(int a, int b, int c) {
        return Math.min(a, Math.min(b, c));
    }
}

最小覆盖子串

滑动窗口

class Solution {
    public String minWindow(String s, String t) {
        int left = 0, right = 0;
        int meet = 0;
        int start = 0, end = 0;
        int minLen = Integer.MAX_VALUE;
        Map<Character, Integer> cover = new HashMap<>();
        Map<Character, Integer> window = new HashMap<>();

        // cover统计s中各字符出现的次数
        for (char c : t.toCharArray()) {
            cover.put(c, cover.getOrDefault(c, 0) + 1);
        }

        // 滑动窗口
        while (right < s.length()) {
            char c = s.charAt(right);
            right++;
            if (cover.containsKey(c)) {
                window.put(c, window.getOrDefault(c, 0) + 1);
                if (cover.get(c).equals(window.get(c))) {
                    meet++;
                }
            }
            // 满足覆盖,开始缩小窗口求最小的覆盖子串
            while (meet == cover.size()) {
                // 更新最小窗口大小
                if (right - left < minLen) {
                    start = left;
                    minLen = right - left;
                }
                // 缩小窗口
                char d = s.charAt(left);
                left++;
                // 更新窗口内字符的出现次数
                if (cover.containsKey(d)) {
                    // left右移后将不再满足覆盖条件,会退出内循环
                    if (cover.get(d).equals(window.get(d))) {
                        meet--;
                    }
                    // d在窗口内的出现次数-1
                    window.put(d, window.getOrDefault(d, 0) - 1);
                }
            }
        }

        return minLen == Integer.MAX_VALUE ? "" : s.substring(start, start + minLen);
    }
}

柱状图中最大的矩形

暴力方法O(n^2) —— 超时

class Solution {
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        int maxRecArea = 0;
        
        for (int i = 0; i < n; i++) {
            int left = i;
            // 往左边扩展
            while (left > 0 && heights[left - 1] >= heights[i]) {
                left--;
            }
            int right = i;
            // 往右边扩展
            while (right < n - 1 && heights[right + 1] >= heights[i]) {
                right++;
            }
            int width = (right - left + 1);
            maxRecArea = Math.max(maxRecArea, width * heights[i]);
        }
        return maxRecArea;

    }
}

单调递增栈——空间换时间 O(n),O(n)

class Solution {
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        Deque<Integer> stk = new ArrayDeque<>();
        int[] newHeights = new int[n + 2];
        int maxRecArea = 0;
        // 新构造的数组首位添0
        newHeights[0] = 0;
        newHeights[n + 1] = 0;
        for (int i = 1; i <= n; i++) {
            newHeights[i] = heights[i - 1];
        }

        for (int i = 0; i < n + 2; i++) {
            while (!stk.isEmpty() && newHeights[i] < newHeights[stk.peek()]) {
                int cur = stk.pop();
                int curH = newHeights[cur];
                int left = stk.peek();
                int right = i;
                int recWidth = right - left - 1;
                maxRecArea = Math.max(maxRecArea, recWidth * curH);
            }
            // 单调栈存放的是高度的下标
            stk.push(i);
        }
        return maxRecArea;
    }
}

最大矩形

image-20210524194244954

求出每一层的heights,利用上一题的解法去解即可

class Solution {
    public int maximalRectangle(char[][] matrix) {
        if (matrix.length == 0) return 0;
        int m = matrix.length, n = matrix[0].length;
        int[] heights = new int[n];
        int maxArea = 0;
        for (int row = 0; row < m; row++) {
            for (int col = 0; col < n; col++) {
                if (matrix[row][col] == '1') {
                    heights[col] += 1;
                } else {
                    heights[col] = 0;
                }
            }
            maxArea = Math.max(maxArea, largestRectangleArea(heights));
        }
        return maxArea;
        
    }

    // 利用LC84-柱状图中最大的矩形的单调栈解法
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        Deque<Integer> stk = new ArrayDeque<>();
        int[] newHeights = new int[n + 2];
        int maxRecArea = 0;
        // 新构造的数组首位添0
        newHeights[0] = 0;
        newHeights[n + 1] = 0;
        for (int i = 1; i <= n; i++) {
            newHeights[i] = heights[i - 1];
        }

        for (int i = 0; i < n + 2; i++) {
            while (!stk.isEmpty() && newHeights[i] < newHeights[stk.peek()]) {
                int cur = stk.pop();
                int curH = newHeights[cur];
                int left = stk.peek();
                int right = i;
                int recWidth = right - left - 1;
                maxRecArea = Math.max(maxRecArea, recWidth * curH);
            }
            // 单调栈存放的是高度的下标
            stk.push(i);
        }
        return maxRecArea;
    }
}

二叉树中的最大路径和

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    int res = Integer.MIN_VALUE;
    public int maxPathSum(TreeNode root) {
        //只有一个根节点
        if (root.left == null && root.right == null) return root.val;
        dfs(root);

        return res;
    }

    public int dfs(TreeNode node){
        if (node == null) return 0;
        
        int leftSum = dfs(node.left);
        int rightSum = dfs(node.right);
		
        // 单边和用于返回
        int cur = Math.max(node.val, Math.max(leftSum + node.val, rightSum + node.val));
        //更新最大路径和
        res = Math.max(res, Math.max(cur, node.val + leftSum + rightSum));
        return cur;
    }
}

最长连续序列

并查集

import java.util.HashMap;
import java.util.Map;

public class Solution {

    private class UnionFind {
        private Map<Integer, Integer> parent;
        private Map<Integer, Integer> size;

        public UnionFind(int[] nums) {
            int len = nums.length;
            parent = new HashMap<>(len);
            size = new HashMap<>(len);
            for (int num : nums) {
                parent.put(num, num);
                size.put(num, 1);
            }
        }

        //拼接树,将"重量小"的树根加到"重量大"的树下
        public int union(int x, int y) {
            int rootX = find(x);
            int rootY = find(y);
            //同一棵树,不需要拼接
            if (rootX == rootY) {
                return 0;
            }
            //分别计算两棵树重量
            int sizeX = size.get(rootX), sizeY = size.get(rootY);
            int sum = sizeX + sizeY;
            //将x的树根rootX拼接到rootY下
            if (sizeX <= sizeY){
                parent.put(rootX, rootY);
                //更新拼接后的树的"重量"
                size.put(rootY, sum);
            } else {
                //将y的树根rootY拼接到rootX下
                parent.put(rootY, rootX);
                //更新拼接后的树的"重量"
                size.put(rootX, sum);
            }
            return sum;
        }

        //找当前节点的根节点,用到了路径压缩
        public int find(int x) {
            while (x != parent.get(x)) {
                parent.put(x, parent.get(parent.get(x)));
                x = parent.get(x);
            }
            return x;
        }

        public boolean contains(int x) {
            return parent.containsKey(x);
        }
    }

    public int longestConsecutive(int[] nums) {
        int len = nums.length;
        if (len == 0) {
            return 0;
        }
        UnionFind unionFind = new UnionFind(nums);

        int res = 1;

        for (int num : nums) {
            if (unionFind.contains(num - 1)) {
                res = Math.max(res, unionFind.union(num, num - 1));
            }

            if (unionFind.contains(num + 1)) {
                res = Math.max(res, unionFind.union(num, num + 1));
            }
        }
        return res;
    }
}

滑动窗口最大值


二叉树的序列化和反序列化

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Codec {

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        if (root == null) return "[]";
        //层次遍历
        StringBuilder sb = 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 node = q.poll();
                if (node != null) {
                    sb.append(node.val);
                    q.offer(node.left);
                    q.offer(node.right);
                } else {
                    sb.append("null");
                }
                sb.append(",");
            }
        }
        return sb.deleteCharAt(sb.length() - 1).append("]").toString();
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        //data为null或者"[]"的情况
        if (data == null || "[]".equals(data)) {
            return null;
        }
        //去[],按","分割字符串
        String[] nodes = data.substring(1, data.length() - 1).split(",");
        int n = nodes.length;
        Deque<TreeNode> queue = new ArrayDeque<>();
        TreeNode root = new TreeNode(Integer.parseInt(nodes[0]));
        queue.offer(root);
        int cnt = 1;
        while (!queue.isEmpty() && cnt < n){
            TreeNode node = queue.poll();
            if (!"null".equals(nodes[cnt])){
                node.left = new TreeNode(Integer.parseInt(nodes[cnt]));
                queue.offer(node.left);
            }
            cnt++;
            if (!"null".equals(nodes[cnt])){
                node.right = new TreeNode(Integer.parseInt(nodes[cnt]));
                queue.offer(node.right);
            }
            cnt++;

        }
        return root;
        

    }

    
}

// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// TreeNode ans = deser.deserialize(ser.serialize(root));

删除无效的括号


戳气球


posted @ 2021-06-07 21:10  打瞌睡的布偶猫  阅读(456)  评论(0编辑  收藏  举报