代码随想录刷题笔记

代码随想录

数组

704. 二分查找

力扣题目链接
在这里插入图片描述

class Solution {
    public int search(int[] nums, int target) {
        int left = 0, right = nums.length-1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return -1;
    }
}

27. 移除元素

力扣题目链接
在这里插入图片描述

class Solution {
    public int removeElement(int[] nums, int val) {
        // 双指针 快慢指针
        int slow = 0;
        for (int fast = 0; fast < nums.length; fast++) {
            if (val != nums[fast]) {
                nums[slow++] = nums[fast];
            }
        }
        return slow; // slow恰好是最后得到的数组长度 
    }
}

977. 有序数组的平方

力扣题目链接
在这里插入图片描述

class Solution {
    public int[] sortedSquares(int[] nums) {
        // 双指针法
        // 数组平方的最大值一定在两端
        int[] ans = new int[nums.length];
        int left = 0, right = nums.length - 1;
        int i = nums.length - 1;
        while (left <= right) {
            if (nums[left] * nums[left] <= nums[right] * nums[right]) {
                ans[i--] = nums[right] * nums[right];
                right--;
            } else{
                ans[i--] = nums[left] * nums[left];
                left++;
            }
        } 
        return ans;
    }
}

209. 长度最小的子数组

力扣题目链接
在这里插入图片描述

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int ans = Integer.MAX_VALUE; // 要返回的最短子数组长度
        int subLength = 0; // 子数组的长度
        int subSum = 0; // 滑动窗口的和
        int i = 0; // 滑动窗口左边界
        for (int j = 0; j < nums.length; j++) {
            subSum += nums[j];
            while (subSum >= target) { // 滑窗左边界右移的条件是子数组的和大于target
                subLength = j - i + 1; // 子数组长度
                ans = subLength < ans ? subLength : ans;
                subSum -= nums[i++]; // 左边界右移
            }
        }
        return ans == Integer.MAX_VALUE ? 0 : ans;
    }
}

59.螺旋矩阵II

力扣题目链接
在这里插入图片描述

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] ans = new int[n][n];
        int num = 1;
        int target = n * n;
        int i = 0, j = 0;
        while(num <= target){
            for(; j < n && ans[i][j] == 0; j++) ans[i][j] = num++;
            i++; j--;
            for(; i < n && ans[i][j] == 0; i++) ans[i][j] = num++;
            i--; j--;
            for(; j >= 0 && ans[i][j] == 0; j--) ans[i][j] = num++;
            i--; j++;
            for(; i >= 0 && ans[i][j] == 0; i--) ans[i][j] = num++;
            i++; j++;
        }
        return ans;
    }
}

总结

五道题分别是:二分法、双指针法、双指针法、滑动窗口、模拟

链表

203. 移除链表元素

力扣题目链接
在这里插入图片描述

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        ListNode preHead = new ListNode(); // 定义一个指向头结点的结点
        preHead.next = head;
        ListNode p = head, q = preHead; // p 指向当前遍历的结点,q指向前一个结点
        while (p != null) {
            if (p.val == val) {
                p = p.next;
                q.next = p;
            }  else {
                p = p.next;
                q = q.next;   
            }
        }
        return preHead.next;
    }
}

707. 设计链表

力扣题目链接
在这里插入图片描述

class ListNode {
    int val;
    ListNode next;
    ListNode(){}
    ListNode(int val){this.val=val;}
}
class MyLinkedList {
    int size;
    ListNode head;
    public MyLinkedList() {
        size = 0;
        head = new ListNode(); // 虚拟的头结点,并不是首元结点
    }    
    public int get(int index) {
        // 若链表是1 2 3,get(0)得到1
        if (index < 0 || index >= size) return -1;
        ListNode p = head;
        // p需要向前走index+1步
        for (int i = 0; i < index + 1; i++) {
            p = p.next;
        }
        return p.val;
    }    
    public void addAtHead(int val) {
        addAtIndex(0, val);
    }    
    public void addAtTail(int val) {
        addAtIndex(size, val);
    }    
    public void addAtIndex(int index, int val) {
        if (index > size) return;
        ListNode p = head;
        for (int i = 0; i < index; i++) {
            p = p.next;
        }
        // 此时p指向的是要插入位置的前一个结点
        ListNode p_next = p.next;
        ListNode insertNode = new ListNode(val); // 要插入的结点
        p.next = insertNode;
        insertNode.next = p_next;
        size++;
    }    
    public void deleteAtIndex(int index) {
        if (index < 0 || index >= size) return;
        ListNode p = head;
        for (int i = 0; i < index; i++) {
            p = p.next;
        }
        // 此时p指向的是要删除位置的前一个结点
        ListNode p_next_next = p.next.next;
        p.next = p_next_next;
        size--;
    }
}

206. 反转链表

力扣题目链接
在这里插入图片描述

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode curr = head; // 指向当前遍历结点
        ListNode prev = null; // 指向cur之前的结点
        ListNode next; // 用来指向curr的下一个结点
        while (curr != null) {
            next = curr.next;
            curr.next = prev;
            prev = curr;
            curr = next;
        }
        return prev;
    }
}

24. 两两交换链表中的节点

力扣题目链接

在这里插入图片描述

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode preHead = new ListNode();
        preHead.next = head;
        ListNode curr = preHead;
        while (curr.next != null && curr.next.next != null) {
            ListNode temp = curr.next; // 这一组相邻结点的第一个结点
            ListNode temp2 = curr.next.next.next; // 下一组的第一个结点
            curr.next = temp.next;
            temp.next.next = temp;
            temp.next = temp2;
            curr = temp;
        }
        return preHead.next;
    }
}

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

力扣题目链接

在这里插入图片描述

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode fast = head;
        ListNode slow = new ListNode(0, head);
        ListNode preHead = slow;
        for(int i = 0; i < n; i++) {
            fast = fast.next;
        }
        while (fast != null) {
            fast = fast.next;
            slow = slow.next;
        }
        // 删除slow.next结点
        slow.next = slow.next.next;
        return preHead.next;
    }
}

面试题 02.07. 链表相交

力扣题目链接
在这里插入图片描述

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode p = headA, q = headB;
        while (p != q) {
            p = (p != null) ? p.next : headB;
            q = (q != null) ? q.next : headA;
        }
        return p;
    }
}

142. 环形链表 II

力扣题目链接

题目:给定一个链表,如果有环,返回环的起始点,如果没有环,返回null

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head, slow = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) { // 快慢指针相遇了,说明有环
                ListNode p = fast; // 快慢指针相遇处
                ListNode q = head; // 头结点和相遇处一起走
                while (p != q) {
                    p = p.next;
                    q = q.next;
                }
                return p;
            }
        }
        return null;
    }
}

哈希表

242. 有效的字母异位词

力扣题目链接
在这里插入图片描述

class Solution {
    public boolean isAnagram(String s, String t) {
        int[] record = new int[26];
        for (int i = 0; i < s.length(); i++) {
            record[s.charAt(i) - 'a']++;
        }
        for (int i = 0; i < t.length(); i++) {
            record[t.charAt(i) - 'a']--;
        }
        for (int i = 0; i < 26; i++) {
            if (record[i] != 0) return false;
        }
        return true;
    }
}

349. 两个数组的交集

力扣题目链接
在这里插入图片描述

202. 快乐数

https://leetcode.cn/problems/happy-number/

在这里插入图片描述

class Solution {
    public boolean isHappy(int n) {
        Set<Integer> set = new HashSet<>();
        while (n != 1 && !set.contains(n)) { // 如果集合中包含了当前数,那么就出现循环了
            set.add(n);
            n = getNext(n);
        }
        return n == 1;
    }
    private int getNext(int n) {
        int res = 0;
        while (n > 0) {
            int temp = n % 10;
            n = n / 10;
            res += temp * temp;
        }
        return res;
    }
}

1. 两数之和

https://leetcode.cn/problems/two-sum/
在这里插入图片描述

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

454. 四数相加 II

https://leetcode.cn/problems/4sum-ii/
在这里插入图片描述

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        Map<Integer, Integer> map = new HashMap<>(); // 存放数组1和2的和及其出现的次数
        int res = 0;
        // 遍历数组1和2
        for (int i: nums1) {
            for (int j: nums2) {
                int sum = i + j;
                map.put(sum, map.getOrDefault(sum, 0) + 1);
            }
        }
        // 遍历数组3和4
        for (int i: nums3) {
            for (int j: nums4) {
                int sum = i + j;
                res += map.getOrDefault(-sum, 0);
            }
        }
        return res;
    }
}

383. 赎金信

https://leetcode.cn/problems/ransom-note
在这里插入图片描述

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        int[] map = new int[26];
        for (char c: magazine.toCharArray()) {
            map[c - 'a'] += 1;
        }
        for (char c: ransomNote.toCharArray()) {
            map[c - 'a'] -= 1;
        }
        for (int i: map) {
            if (i < 0) {
                return false;
            }
        }
        return true;
    }
}

15. 三数之和

https://leetcode.cn/problems/3sum/
在这里插入图片描述

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        int n = nums.length;
        // 先排序
        Arrays.sort(nums);
        for (int i = 0; i < n - 2; i++) {
            if (i > 0 && nums[i] == nums[i-1]) continue; // 去重
            if (nums[i] + nums[i+1] + nums[i+2] > 0) break;
            if (nums[i] + nums[n-1] + nums[n-2] < 0) continue;
            int j = i + 1, k = n - 1;
            while (j < k) {
                int sum = nums[i] + nums[j] + nums[k];
                if (sum > 0) {
                    k--;
                } else if (sum < 0) {
                    j++;
                } else {
                    res.add(Arrays.asList(nums[i], nums[j], nums[k]));
                    for (j++; j < k && nums[j] == nums[j-1]; j++); // 去重
                    for (k--; j < k && nums[k] == nums[k+1]; k--); // 去重
                }
            }
        }
        return res;
    }
}

18. 四数之和

https://leetcode.cn/problems/4sum/
在这里插入图片描述

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(nums);
        int n = nums.length;
        for (int a = 0; a < n - 3; a++) { // 枚举第一个数
            if (a > 0 && nums[a] == nums[a-1]) continue; // 去重
            if ((long) nums[a] + nums[a+1] + nums[a+2] + nums[a+3] > target) break;
            if ((long) nums[a] + nums[n-1] + nums[n-2] + nums[n-3] < target) continue;
            for (int b = a + 1; b < n - 2; b++) {
                if (b > a + 1 && nums[b] == nums[b-1]) continue; // 去重
                if ((long) nums[a] + nums[b] + nums[b+1] + nums[b+2] > target) break;
                if ((long) nums[a] + nums[b] + nums[n-1] + nums[n-2] < target) continue;
                int c = b + 1, d = n - 1;
                while (c < d) {
                    long sum =  nums[a] + nums[b] + nums[c] + nums[d];
                    if (sum > target) d--;
                    else if (sum < target) c++;
                    else {
                        res.add(List.of(nums[a], nums[b], nums[c], nums[d]));
                        for (c++; c < d && nums[c] == nums[c-1]; c++); // 去重
                        for (d--; c < d && nums[d] == nums[d+1]; d--); // 去重
                    }
                }
            }
        }
        return res;
    }
}

字符串

344. 反转字符串

https://leetcode.cn/problems/reverse-string
在这里插入图片描述

class Solution {
    public void reverseString(char[] s) {
        for (int i = 0, j = s.length; i < j; i++, j--) {
            char temp = s[i];
            s[i] = s[j];
            s[j] = temp;
        }
    }
}

541. 反转字符串 II

https://leetcode.cn/problems/reverse-string-ii/
在这里插入图片描述

class Solution {
    public String reverseStr(String s, int k) {
        int n = s.length();
        char[] ch = s.toCharArray();
        for (int i = 0; i < n; i += (2 * k)) {
            if (n - i < k) { // 剩余字符的数量为n - start
                reverseString(ch, i, n - 1);
            } else {
                reverseString(ch, i, i + k - 1);
            }
        }
        return new String(ch);
    }
    public void reverseString(char[] s, int start, int end) {
        // 反正s字符串中从start到end的
        for (int i = start, j = end; i < j; i++, j--) {
            char temp = s[i];
            s[i] = s[j];
            s[j] = temp;
        }
    }
}

剑指 Offer 05. 替换空格

https://leetcode.cn/problems/ti-huan-kong-ge-lcof/
在这里插入图片描述

class Solution {
    public String replaceSpace(String s) {
        int count = 0; // 统计空格数
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == ' ') {
                count++;
            }
        }
        char[] ch = new char[s.length() + count * 2]; // 新数组长度
        for (int i = 0, j = 0; i < s.length(); i++) { // i是旧数组的,j是新数组的
            if (s.charAt(i) == ' ') {
                ch[j++] = '%';
                ch[j++] = '2';
                ch[j++] = '0';
            } else {
                ch[j++] = s.charAt(i);
            }
        }
        return new String(ch);
    }
}

151. 反转字符串中的单词

https://leetcode.cn/problems/reverse-words-in-a-string/
在这里插入图片描述

class Solution {
    public String reverseWords(String s) {
        // 1、移除多余空格(移除首位空格,单词间只保留一个空格)
        StringBuilder sb = removeExtraSpace(s);
        // 2、字符串反转
        reverseString(sb, 0, sb.length() - 1);
        // 3、单词反转
        reverseEachWord(sb);
        return sb.toString();
    }
    private StringBuilder removeExtraSpace(String s) {
        int start = 0, end = s.length() - 1;
        // 去除首尾空格
        while (s.charAt(start) == ' ') start++;
        while (s.charAt(end) == ' ') end--;
        StringBuilder sb = new StringBuilder();
        while (start <= end) {
            if (s.charAt(start) != ' ' || s.charAt(start - 1) != ' ') {
                // 如果不是空格或者是单词后的第一个空格,就添加进去,这样就去除掉了单词间多余空格
                sb.append(s.charAt(start));
            }
            start++;
        }
        return sb;
    }
    private void reverseString(StringBuilder sb, int start, int end) {
        while (start <= end) {
            char temp = sb.charAt(start);
            sb.setCharAt(start, sb.charAt(end));
            sb.setCharAt(end, temp);
            start++;
            end--;
        }
    }
    private void reverseEachWord(StringBuilder sb) {
        int n = sb.length();
        int start = 0;
        while (start < n) {
            int end = start;
            while (end < n && sb.charAt(end) != ' ') end++;
            reverseString(sb, start, end - 1);
            start = end + 1;
        }
    }
}

剑指 Offer 58 - II. 左旋转字符串

https://leetcode.cn/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/
在这里插入图片描述

class Solution {
    public String reverseLeftWords(String s, int n) {
        char[] ch = s.toCharArray();
        reverseString(ch, 0, n - 1);
        reverseString(ch, n, s.length() - 1);
        reverseString(ch, 0, s.length() - 1);
        return new String(ch);
    }
    private void reverseString(char[] ch, int start, int end) {
        while (start < end) {
            char temp = ch[start];
            ch[start] = ch[end];
            ch[end] = temp;
            start++;
            end--;
        }
    }
}

28. 找出字符串中第一个匹配项的下标

https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string
在这里插入图片描述
这是经典的KMP算法

栈与队列

232. 用栈实现队列

https://leetcode.cn/problems/implement-queue-using-stacks/
在这里插入图片描述

class MyQueue {
    Stack<Integer> inStack;
    Stack<Integer> outStack;
    public MyQueue() {
        inStack = new Stack<>();
        outStack = new Stack<>();
    }
    
    public void push(int x) {
        inStack.push(x);
    }
    
    public int pop() {
        inToOut();
        return outStack.pop();
    }
    
    public int peek() {
        inToOut();
        return outStack.peek();
    }
    
    public boolean empty() {
        return inStack.isEmpty() && outStack.isEmpty();
    }

    private void inToOut() { // 将in中的放入到out中
        if (!outStack.isEmpty()) return; // 如果out不为空,直接返回
        while (!inStack.isEmpty()) {
            outStack.push(inStack.pop());
        }
    }
}

225. 用队列实现栈

https://leetcode.cn/problems/implement-stack-using-queues
在这里插入图片描述

class MyStack {
    Queue<Integer> queue1;
    Queue<Integer> queue2;
    
    public MyStack() {
        queue1 = new LinkedList<>();
        queue2 = new LinkedList<>();
    }
    
    public void push(int x) {
        queue2.offer(x);
        while (!queue1.isEmpty()) {
            queue2.offer(queue1.poll());
        }
        Queue<Integer> temp = queue1;
        queue1 = queue2;
        queue2 = temp;
    }
    
    public int pop() {
        return queue1.poll();
    }
    
    public int top() {
        return queue1.peek();
    }
    
    public boolean empty() {
        return queue1.isEmpty();
    }
}

20. 有效的括号

https://leetcode.cn/problems/valid-parentheses/
在这里插入图片描述

class Solution {
    public boolean isValid(String s) {
        Deque<Character> deque = new LinkedList<>();
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if (ch == '(') {
                deque.push(')');
            } else if (ch == '[') {
                deque.push(']');
            } else if (ch == '{') {
                deque.push('}');
            } else if (deque.isEmpty() || deque.peek() != ch) {
                return false;
            } else {
                deque.pop();
            }
        }
        return deque.isEmpty();
    }
}

1047. 删除字符串中的所有相邻重复项

https://leetcode.cn/problems/remove-all-adjacent-duplicates-in-string
在这里插入图片描述

class Solution {
    public String removeDuplicates(String s) {
        char[] chs = s.toCharArray();
        int top = -1;
        for (int i = 0; i < s.length(); i++) {
            if (top == -1 || chs[top] != chs[i]) {
                chs[++top] = chs[i];
            } else {
                top--;
            }
        }
        return String.valueOf(chs, 0, top+1);
    }
}

150. 逆波兰表达式求值

https://leetcode.cn/problems/evaluate-reverse-polish-notation/
在这里插入图片描述

class Solution {
    public int evalRPN(String[] tokens) {
        Deque<Integer> stack = new LinkedList<>();
        for (String s: tokens) {
            if (s.equals("+")) {
                stack.push(stack.pop() + stack.pop());
            } else if (s.equals("-")) {
                stack.push(-stack.pop() + stack.pop());
            } else if (s.equals("*")) {
                stack.push(stack.pop() * stack.pop());
            } else if (s.equals("/")) {
                int num2 = stack.pop();
                int num1 = stack.pop();
                stack.push(num1 / num2);
            } else {
                stack.push(Integer.valueOf(s));
            }
        }
        return stack.pop();
    }
}

239. 滑动窗口最大值

https://leetcode.cn/problems/sliding-window-maximum/
在这里插入图片描述
方法一:优先队列

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        // pq存放nums[i]与i
        PriorityQueue<int[]> pq = new PriorityQueue<>(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                // 排序方式:先按照值升序排序,如果值相同则按照下标升序排序
                if (o1[0] != o2[0]) {
                    return o2[0] - o1[0];
                } else {
                    return o2[1] - o1[1];
                }
            }
        });
        // 先把第一组k个数放到优先队列中
        for (int i = 0; i < k; i++) {
            pq.offer(new int[]{nums[i], i});
        }
        int[] ans = new int[n - k + 1];
        ans[0] = pq.peek()[0]; // 第一组k个数的最大值
        for (int i = k; i < n; i++) {
            pq.offer(new int[]{nums[i], i});
            // 插入一个元素后,若最大值的下标不在滑窗中,就要把前面的都移出去
            while (pq.peek()[1] <= i - k) {
                pq.poll();
            }
            ans[i - k + 1] = pq.peek()[0];
        }
        return ans;
    }
}

方法二:单调队列

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        Deque<Integer> deque = new LinkedList<>();
        for (int i = 0; i < k; i++) {
            while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]){
                // 如果右侧的新值大于左侧的旧值,那么左侧的旧值就可以永久删除了
                deque.pollLast();
            }
            deque.offerLast(i);
        } // for结束后,deque存储的下标按照从小到大的顺序存储,并且它们在数组nums中对应的值是递减的
        int[] ans = new int[n - k + 1];
        ans[0] = nums[deque.peekFirst()];
        for (int i = k; i < n; i++) {
            // 重复上一个for循环的内容
            while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]){
                deque.pollLast();
            }
            deque.offerLast(i);
            while (deque.peekFirst() <= i - k) {
                deque.pollFirst();
            }
            ans[i - k + 1] = nums[deque.peekFirst()];
        }
        return ans;
    }
}

347. 前 K 个高频元素

https://leetcode.cn/problems/top-k-frequent-elements/
在这里插入图片描述

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int num: nums) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        PriorityQueue<int[] > pq = new PriorityQueue<>(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[1] - o2[1];
            }
        }); // 优先队列,根据频率从小到大排
        for (var x : map.entrySet()) {
            pq.offer(new int[]{x.getKey(), x.getValue()});
            if (pq.size() > k) {
                pq.poll();
            }
        }
        int[] res = new int[k];
        for (int i = 0; i < k; i++) {
            res[i] = pq.poll()[0];
        }
        return res;
    }
}

二叉树

144. 二叉树的前序遍历

https://leetcode.cn/problems/binary-tree-preorder-traversal/

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) return res;
        Deque<TreeNode> stack = new LinkedList<>();
        stack.push(root);
        while (!stack.isEmpty()) {
            TreeNode temp = stack.pop();
            res.add(temp.val);
            if (temp.right != null) stack.push(temp.right);
            if (temp.left != null) stack.push(temp.left);
        }
        return res;
    }
}

94. 二叉树的中序遍历

https://leetcode.cn/problems/binary-tree-inorder-traversal/

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) return res;
        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode cur = root;
        while (cur != null || !stack.isEmpty()) {
            if (cur != null) { // 先一直向左走到头
                stack.push(cur);
                cur = cur.left;
            } else {
                cur = stack.pop();
                res.add(cur.val);
                cur = cur.right;
            }
        }
        return res;
    }
}

145. 二叉树的后序遍历

https://leetcode.cn/problems/binary-tree-postorder-traversal/

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root != null) stack.push(root);
        Deque<TreeNode> stack = new LinkedList<>();
        
        while (!stack.isEmpty()) {
            TreeNode temp = stack.pop();
            res.add(temp.val);
            if (temp.left != null) stack.push(temp.left); // 跟前序反过来
            if (temp.right != null) stack.push(temp.right);
        }
        Collections.reverse(res); // 逆序
        return res;
    }
}

102. 二叉树的层序遍历(模板)

https://leetcode.cn/problems/binary-tree-level-order-traversal/

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        Deque<TreeNode> deque = new LinkedList<>();
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        if (root == null) return res;
        deque.offer(root);
        while (!deque.isEmpty()) {
	        List<Integer> temp = new ArrayList<>(); // 存放一层的结点值
	        // 对一层进行遍历
	        int len = deque.size();
	        for (int i = 0; i < len; i++) {
	            TreeNode node = deque.poll();
	            temp.add(node.val);
	            if (node.left != null) deque.offer(node.left);
	            if (node.right != null) deque.offer(node.right);
	        }
	        res.add(temp);
        }
        return res;
    }
}

107. 二叉树的层序遍历 II

https://leetcode.cn/problems/binary-tree-level-order-traversal-ii/
在这里插入图片描述

class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        Deque<TreeNode> deque = new LinkedList<>();
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        if (root == null) return res;
        deque.offer(root);
        while (!deque.isEmpty()) {
            List<Integer> temp = new ArrayList<>(); // 存放一层的结点值
            // 对一层进行遍历
            int len = deque.size();
            for (int i = 0; i < len; i++) {
                TreeNode node = deque.poll();
                temp.add(node.val);
                if (node.left != null) deque.offer(node.left);
                if (node.right != null) deque.offer(node.right);
            }
            res.add(temp);
        }
        Collections.reverse(res);
        return res;
    }
}

199. 二叉树的右视图

https://leetcode.cn/problems/binary-tree-right-side-view/
在这里插入图片描述

class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        Deque<TreeNode> deque = new LinkedList<>();
        List<Integer> res = new ArrayList<>();
        if (root == null) return res;
        deque.offer(root);
        while (!deque.isEmpty()) {
            // 对一层进行遍历
            int len = deque.size();
            for (int i = 0; i < len; i++) {
                TreeNode node = deque.poll();
                if (i == len - 1) {
                    res.add(node.val);
                }
                if (node.left != null) deque.offer(node.left);
                if (node.right != null) deque.offer(node.right);
            }
        }
        return res;
    }
}

637. 二叉树的层平均值

https://leetcode.cn/problems/average-of-levels-in-binary-tree/
在这里插入图片描述

class Solution {
    public List<Double> averageOfLevels(TreeNode root) {
        Deque<TreeNode> deque = new LinkedList<>();
        List<Double> res = new ArrayList<>();
        if (root == null) return res;
        deque.offer(root);
        while (!deque.isEmpty()) {
            int len = deque.size();
            long sum = 0;
            for (int i = 0; i < len; i++) {
                TreeNode node = deque.poll();
                sum += node.val;
                if (node.left != null) deque.offer(node.left);
                if (node.right != null) deque.offer(node.right);
            }
            res.add(1.0 * sum / len);
        }
        return res;
    }
}

429. N 叉树的层序遍历

https://leetcode.cn/problems/n-ary-tree-level-order-traversal/
在这里插入图片描述

class Solution {
    public List<List<Integer>> levelOrder(Node root) {
        Deque<Node> deque = new LinkedList<>();
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        if (root == null) return res;
        deque.offer(root);
        while (!deque.isEmpty()) {
	        List<Integer> temp = new ArrayList<>(); // 存放一层的结点值
	        // 对一层进行遍历
	        int len = deque.size();
	        for (int i = 0; i < len; i++) {
	            Node node = deque.poll();
	            temp.add(node.val);
	            for (Node children: node.children) {
                    if (children != null)
                        deque.offer(children);
                }
	        }
	        res.add(temp);
        }
        return res;
    }
}

515. 在每个树行中找最大值

https://leetcode.cn/problems/find-largest-value-in-each-tree-row/
在这里插入图片描述

class Solution {
    public List<Integer> largestValues(TreeNode root) {
        Deque<TreeNode> deque = new LinkedList<>();
        List<Integer> res = new ArrayList<>();
        if (root == null) return res;
        deque.offer(root);
        while (!deque.isEmpty()) {
	        int maxx = -Integer.MAX_VALUE-1;
	        // 对一层进行遍历
	        int len = deque.size();
	        for (int i = 0; i < len; i++) {
	            TreeNode node = deque.poll();
	            maxx = Math.max(maxx, node.val);
	            if (node.left != null) deque.offer(node.left);
	            if (node.right != null) deque.offer(node.right);
	        }
	        res.add(maxx);
        }
        return res;
    }
}

116. 填充每个节点的下一个右侧节点指针

https://leetcode.cn/problems/populating-next-right-pointers-in-each-node/
在这里插入图片描述

class Solution {
    public Node connect(Node root) {
        Deque<Node> deque = new LinkedList<>();
        if (root == null) return root;
        deque.offer(root);
        while (!deque.isEmpty()) {
	        int len = deque.size();
            Node node = null;
            Node nodePre = null;
	        for (int i = 0; i < len; i++) {
                node = deque.poll();
                if (i != 0) nodePre.next = node;
                nodePre = node;
	            if (node.left != null) deque.offer(node.left);
	            if (node.right != null) deque.offer(node.right);
	        }
        }
        return root;
    }
}

117. 填充每个节点的下一个右侧节点指针 II

https://leetcode.cn/problems/populating-next-right-pointers-in-each-node-ii/
在这里插入图片描述
与上一题不同的地方在于:本题不是完全二叉树,但是不影响,和上一题代码完全一样

class Solution {
    public Node connect(Node root) {
        Deque<Node> deque = new LinkedList<>();
        if (root == null) return root;
        deque.offer(root);
        while (!deque.isEmpty()) {
	        int len = deque.size();
            Node node = null;
            Node nodePre = null;
	        for (int i = 0; i < len; i++) {
                node = deque.poll();
                if (i != 0) nodePre.next = node;
                nodePre = node;
	            if (node.left != null) deque.offer(node.left);
	            if (node.right != null) deque.offer(node.right);
	        }
        }
        return root;
    }
}

104. 二叉树的最大深度

https://leetcode.cn/problems/maximum-depth-of-binary-tree/
在这里插入图片描述

深度优先搜索

class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) return 0;
        int leftDepth = maxDepth(root.left);
        int rightDepth = maxDepth(root.right);
        return Math.max(leftDepth, rightDepth) + 1;
    }
}

时间复杂度O(n),n是节点数
空间复杂度O(height),height是树的高度

广度优先搜素

class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) return 0;
        Deque<TreeNode> deque = new LinkedList<>();
        int res = 0;
        deque.offer(root);
        while (!deque.isEmpty()) {
            int len = deque.size();
            for (int i = 0; i < len; i++) {
                TreeNode node = deque.poll();
                if (node.left != null) deque.offer(node.left);
                if (node.right != null) deque.offer(node.right);
            }
            res++;
        }
        return res;
    }
}

时间复杂度O(n),n是节点数
空间复杂度O(n),此方法空间的消耗取决于队列存储的元素数量,其在最坏情况下会达到O(n)

111. 二叉树的最小深度

https://leetcode.cn/problems/minimum-depth-of-binary-tree/
在这里插入图片描述

深度优先搜索

class Solution {
    public int minDepth(TreeNode root) {
        if (root == null) return 0;
        // if (root.left == null && root.right == null) return 1; 这行帮助理解,可以省略
        int m1 = minDepth(root.left);
        int m2 = minDepth(root.right);
        if (root.left == null || root.right == null) return m1 + m2 + 1;
        return Math.min(m1, m2) + 1;
    }
}

广度优先搜索

class Solution {
    public int minDepth(TreeNode root) {
        if (root == null) return 0;
        Deque<TreeNode> deque = new LinkedList<>();
        deque.push(root);
        int res = 0;
        while (!deque.isEmpty()) {
            int len = deque.size();
            res++;
            for (int i = 0; i < len; i++) {
                TreeNode node = deque.poll();
                if (node.left != null) deque.offer(node.left);
                if (node.right != null) deque.offer(node.right);
                if (node.left == null && node.right == null) return res;
            }
        }
        return res;
    }
}

226. 翻转二叉树

https://leetcode.cn/problems/invert-binary-tree/
在这里插入图片描述

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root == null) return root;
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }
}

101. 对称二叉树

https://leetcode.cn/problems/symmetric-tree/
在这里插入图片描述

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null) return true;
        return compare(root.left, root.right);
    }
    public boolean compare(TreeNode left, TreeNode right) {
        if (left == null && right != null) return false;
        else if (left != null && right == null) return false;
        else if (left == null && right == null) return true;
        else if (left.val != right.val) return false;
        return compare(left.left, right.right) && compare(left.right, right.left);
    }
}

222. 完全二叉树的节点个数

https://leetcode.cn/problems/count-complete-tree-nodes/
在这里插入图片描述

class Solution {
    public int countNodes(TreeNode root) {
        if (root == null) return 0;
        int leftDepth = 1;
        int rightDepth = 1;
        TreeNode leftNode = root.left;
        TreeNode rightNode = root.right;
        while (leftNode != null) {
            leftDepth++;
            leftNode = leftNode.left;
        }
        while (rightNode != null) {
            rightDepth++;
            rightNode = rightNode.right;
        }
        if (leftDepth == rightDepth) {
            return (1 << leftDepth) - 1; // 2^n-1, n是树高度
        }
        return countNodes(root.left) + countNodes(root.right) + 1;
    }
}

110. 平衡二叉树

https://leetcode.cn/problems/balanced-binary-tree/
在这里插入图片描述
这里强调一波概念:

  • 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数。
  • 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数。在这里插入图片描述

因为求深度可以从上到下去查 所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中)
本题要求高度,那必然是用后序遍历,首先写一个getHeight(node)函数,如果在递归的过程中发现不是平衡树,就返回-1,具体看代码

class Solution {
    public boolean isBalanced(TreeNode root) {
        if (getHeight(root) == -1) return false;
        return true;
    }
    public int getHeight(TreeNode node) {
        if (node == null) return 0;
        int leftHeight = getHeight(node.left);
        if (leftHeight == -1) return -1;
        int rightHeight = getHeight(node.right);
        if (rightHeight == -1) return -1;
        if (Math.abs(leftHeight - rightHeight) > 1) return -1;
        else return Math.max(leftHeight, rightHeight) + 1;
    }
}

257. 二叉树的所有路径

https://leetcode.cn/problems/binary-tree-paths/
在这里插入图片描述

import java.util.StringJoiner;
class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        List<String> res = new ArrayList<>();
        if (root == null) return res;
        List<Integer> paths = new ArrayList<>();
        dfs(root, paths, res);
        return res;
    }
    public void dfs(TreeNode node, List<Integer> paths, List<String> res) {
        paths.add(node.val); // 前序遍历
        if (node.left != null) {
            dfs(node.left, paths, res); // 递归
            paths.remove(paths.size()-1); // 回溯,把最后一个元素删掉
        }
        if (node.right != null) {
            dfs(node.right, paths, res); // 递归
            paths.remove(paths.size()-1); // 回溯,把最后一个元素删掉
        }
        if (node.left == null && node.right == null) { 
            // 遇到叶子结点,就得到了一条path,把path按照要求格式添加到res中
            res.add(format(paths));
        }
    }
    public String format(List<Integer> paths) {
        // 如果paths=[1,2,3],那么返回"1->2->3"
        StringJoiner s = new StringJoiner("->");
        for (int i = 0; i < paths.size(); i++) {
            s.add(String.valueOf(paths.get(i)));
        }
        return s.toString();
    }
}

404. 左叶子之和

https://leetcode.cn/problems/sum-of-left-leaves/ 在这里插入图片描述

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        if (root == null) return 0;
        return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right) + (root.left != null && root.left.left == null && root.left.right == null? root.left.val: 0);
    }
}

513. 找树左下角的值

https://leetcode.cn/problems/find-bottom-left-tree-value/
在这里插入图片描述

class Solution {
    public int findBottomLeftValue(TreeNode root) {
        Deque<TreeNode> deque = new LinkedList<>();
        int res = root.val;
        deque.offer(root);
        while (!deque.isEmpty()) {
            int len = deque.size();
            for (int i = 0; i < len; i++) {
                TreeNode node = deque.poll();
                if (i == 0) {
                    res = node.val;
                }
                if (node.left != null) deque.offer(node.left);
                if (node.right != null) deque.offer(node.right);
            }
        }
        return res;
    }
}

112. 路径总和

https://leetcode.cn/problems/path-sum/
在这里插入图片描述

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if (root == null) return false;
        return dfs(root, targetSum-root.val);
    }
    public boolean dfs(TreeNode node, int count) {
        if (node.left == null && node.right == null) { // 叶子
            if (count == 0) return true;
            else return false;
        } 
        if (node.left != null) {
            count -= node.left.val;
            if (dfs(node.left, count)) return true;
            count += node.left.val;
        }
        if (node.right != null) {
            count -= node.right.val;
            if (dfs(node.right, count)) return true;
            count += node.right.val;
        }
        return false;
    }
}

精简版:

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if (root == null) return false;
        if (root.left == null && root.right == null && targetSum == root.val) return true;
        return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val);
    }
}

113. 路径总和 II

https://leetcode.cn/problems/path-sum-ii/
在这里插入图片描述

class Solution {
    private List<List<Integer>> res = new ArrayList<>();
    private List<Integer> path = new LinkedList<>();

    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        if (root == null) return res;
        dfs(root, targetSum);
        return res;
    }
    private void dfs(TreeNode node, int targetSum) {
        path.add(node.val);
        if (node.left == null && node.right == null) { // 叶子
            if (targetSum - node.val == 0) { // 找到了一个路径
                res.add(new ArrayList<>(path));
            } 
            return;
        }
        if (node.left != null) {
            dfs(node.left, targetSum - node.val);
            path.remove(path.size() - 1);
        }
        if (node.right != null) {
            dfs(node.right, targetSum - node.val);
            path.remove(path.size() - 1);
        }
    }
}

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

https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder
是同一棵树的中序遍历,请构造二叉树并返回其根节点。

class Solution {
    Map<Integer, Integer> map = new HashMap<>();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        int n = preorder.length; // 节点总数
        for (int i = 0; i < n; i++) {
            map.put(inorder[i], i);
        }
        return myBuildTree(preorder, inorder, 0, n-1, 0, n-1);
    }
    public TreeNode myBuildTree(int[] preorder, int[] inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
        // 四个参数分别代表前序的左右边界(下标),中序的左右边界(下标)
        if (preorder_left > preorder_right) return null;
        int preorder_root = preorder_left; // 根节点在前序遍历的下标
        int root_val = preorder[preorder_root]; // 根节点值
        int inorder_root = map.get(root_val); // 根节点在中序遍历的下标
        TreeNode root = new TreeNode(root_val);
        int len_left = inorder_root - inorder_left; // 左子树的节点数
        root.left = myBuildTree(preorder, inorder, preorder_left+1, preorder_left+len_left, inorder_left, inorder_root-1);
        root.right = myBuildTree(preorder, inorder, preorder_left+len_left+1, preorder_right, inorder_root+1, inorder_right);
        return root;
    }
}

106. 从中序与后序遍历序列构造二叉树

https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/

给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

class Solution {
    Map<Integer, Integer> map = new HashMap<>();
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        int n = inorder.length; // 节点总数
        for (int i = 0; i < n; i++) {
            map.put(inorder[i], i);
        }
        return myBuildTree(inorder, postorder, 0, n-1, 0, n-1);
    }
    public TreeNode myBuildTree(int[] inorder, int[] postorder, int inorder_left, int inorder_right, int postorder_left, int postorder_right) {
        // 四个参数分别代表中序的左右边界(下标),后序的左右边界(下标)
        if (inorder_left > inorder_right || postorder_left > postorder_right) return null;
        int root_val = postorder[postorder_right]; // 根节点值
        int inorder_root = map.get(root_val); // 根节点在中序遍历的下标
        TreeNode root = new TreeNode(root_val);
        int len_left = inorder_root - inorder_left; // 左子树的节点数
        root.left = myBuildTree(inorder, postorder, inorder_left, inorder_root-1, postorder_left, postorder_left+len_left-1);
        root.right = myBuildTree(inorder, postorder, inorder_root+1, inorder_right, postorder_left+len_left, postorder_right-1);
        return root;
    }
}

654. 最大二叉树

https://leetcode.cn/problems/maximum-binary-tree/

给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:
1.创建一个根节点,其值为 nums 中的最大值。
2.递归地在最大值 左边 的 子数组前缀上 构建左子树。
3.递归地在最大值 右边 的 子数组后缀上 构建右子树。
4.返回 nums 构建的 最大二叉树 。

class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        return travelsal(nums, 0, nums.length - 1);
    }
    public TreeNode travelsal(int[] nums, int left_index, int right_index) {
        if (left_index > right_index) {
            return null;
        }
        if (left_index == right_index) { // 只有一个元素
            return new TreeNode(nums[left_index]);
        }
        // 寻找最大值以及它的下标
        int max_value = nums[left_index];
        int max_index = left_index;
        for (int i = left_index + 1; i <= right_index; i++) {
            if (nums[i] > max_value) {
                max_value = nums[i];
                max_index = i;
            }
        }
        TreeNode root = new TreeNode(max_value);
        root.left = travelsal(nums, left_index, max_index - 1);
        root.right = travelsal(nums, max_index + 1, right_index);
        return root;
    }
}

617. 合并二叉树

https://leetcode.cn/problems/merge-two-binary-trees/
在这里插入图片描述

class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if (root1 == null && root2 == null) return null;
        if (root1 == null && root2 != null) return root2;
        if (root1 != null && root2 == null) return root1;
        return new TreeNode(root1.val + root2.val, mergeTrees(root1.left, root2.left), mergeTrees(root1.right, root2.right));
    }
}

700. 二叉搜索树中的搜索

https://leetcode.cn/problems/search-in-a-binary-search-tree/
在这里插入图片描述

class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        if (root == null || root.val == val) return root;
        if (root.val > val) return searchBST(root.left, val);
        if (root.val < val) return searchBST(root.right, val);
        return null;
    }
}

98. 验证二叉搜索树

https://leetcode.cn/problems/validate-binary-search-tree/
在这里插入图片描述

class Solution {
    public boolean isValidBST(TreeNode root) {
        return travelsal(root, Long.MIN_VALUE, Long.MAX_VALUE);
    }
    public boolean travelsal(TreeNode node, long lower, long upper) {
        if (node == null) return true;
        if (node.val <= lower || node.val >= upper) return false;
        return travelsal(node.left, lower, node.val) && travelsal(node.right, node.val, upper);
    }
}

530. 二叉搜索树的最小绝对差

https://leetcode.cn/problems/minimum-absolute-difference-in-bst/
在这里插入图片描述

530. 二叉搜索树的最小绝对差

https://leetcode.cn/problems/minimum-absolute-difference-in-bst/
在这里插入图片描述

class Solution {
    TreeNode pre = null;
    int res = Integer.MAX_VALUE;
    public int getMinimumDifference(TreeNode root) {
        travelsal(root);
        return res;
    }
    public void travelsal(TreeNode cur) {
        if (cur == null) return;
        travelsal(cur.left);
        if (pre != null) res = Math.min(res, cur.val - pre.val);
        pre = cur;
        travelsal(cur.right);
        return;
    }
}

501. 二叉搜索树中的众数

https://leetcode.cn/problems/find-mode-in-binary-search-tree/

class Solution {
    TreeNode pre = null;
    int count = 0;
    int maxCount = 0;
    List<Integer> list = new ArrayList<>(); 
    public int[] findMode(TreeNode root) {
        travelsal(root);
        int[] res = new int[list.size()];
        for (int i = 0; i < res.length; i++) {
            res[i] = list.get(i);
        }
        return res;
    }
    public void travelsal(TreeNode cur) {
        if (cur == null) return;
        travelsal(cur.left);
        if (pre == null) count++;
        else if (cur.val == pre.val) count++;
        else count = 1;
        pre = cur;
        if (count == maxCount) {
            list.add(cur.val);
        } else if (count > maxCount) {
            list.clear();
            list.add(cur.val);
            maxCount = count;
        }
        travelsal(cur.right);
        return;
    }
}

235. 二叉搜索树的最近公共祖先

https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        return travelsal(root, p, q);
    }
    public TreeNode travelsal(TreeNode cur, TreeNode p, TreeNode q) {
        if (cur == null) return null;
        if (cur.val > p.val && cur.val > q.val) {
            TreeNode left = travelsal(cur.left, p, q);
            if (left != null) {
                return left;
            }
        }
        else if (cur.val < p.val && cur.val < q.val) {
            TreeNode right = travelsal(cur.right, p, q);
            if (right != null) {
                return right;
            }
        }
        return cur;
    }
}

236. 二叉树的最近公共祖先

https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == p || root == q || root == null) return root;
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if (left != null && right != null) return root;
        if (left == null) return right;
        return left;
    }
}

701. 二叉搜索树中的插入操作

https://leetcode.cn/problems/insert-into-a-binary-search-tree/
在这里插入图片描述

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        if (root == null) {
            return new TreeNode(val);
        }
        if (root.val < val) {        
            root.right = insertIntoBST(root.right, val);
        } else if (root.val > val) {
            root.left = insertIntoBST(root.left, val);
        }
        return root;
    }
}

450. 删除二叉搜索树中的节点

https://leetcode.cn/problems/delete-node-in-a-bst/
在这里插入图片描述

class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if (root == null) 
            return null;
        if (root.val < key) {
            root.right = deleteNode(root.right, key);
        } else if (root.val > key) {
            root.left = deleteNode(root.left, key);
        } else {
            if (root.left == null && root.right == null) { // 左右都为空
                return null;
            } else if (root.right == null) { // 左不空,右为空
                return root.left;
            } else if (root.left == null) { // 左为空,右不空
                return root.right;
            } else { // 左右都不为空,就将左子树放到右子树的最左边节点的left指针中
                TreeNode cur = root.right;
                while (cur.left != null) {
                    cur = cur.left;
                }
                cur.left = root.left;
                return root.right;
            }
        }
        return root;
    }
}

669. 修剪二叉搜索树

在这里插入图片描述

class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if (root == null) return null;
        if (root.val < low) return trimBST(root.right, low, high);
        if (root.val > high) return trimBST(root.left, low, high);
        root.left = trimBST(root.left, low, high);
        root.right = trimBST(root.right, low, high);
        return root;
    }
}

108. 将有序数组转换为二叉搜索树

https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/
在这里插入图片描述

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        TreeNode root = traversal(nums, 0, nums.length - 1);
        return root;
    }
    public TreeNode traversal(int[] nums, int left, int right) {
        if (left > right) return null;
        int mid = (left + right) / 2;
        TreeNode root = new TreeNode(nums[mid]);
        root.left = traversal(nums, left, mid - 1);
        root.right = traversal(nums, mid + 1, right);
        return root;
    }
}

538. 把二叉搜索树转换为累加树

https://leetcode.cn/problems/convert-bst-to-greater-tree/
在这里插入图片描述

class Solution {
    int pre = 0;
    public TreeNode convertBST(TreeNode root) {
        travelsal(root);
        return root;
    }
    public void travelsal(TreeNode root) {
        if (root == null) return;
        if (root.right != null) travelsal(root.right);
        root.val += pre;
        pre = root.val;
        if (root.left != null) travelsal(root.left);
    }
}

回溯

77. 组合

https://leetcode.cn/problems/combinations/
在这里插入图片描述

class Solution {
    private List<Integer> path = new ArrayList<>();
    private List<List<Integer>> result = new ArrayList<>();
    public List<List<Integer>> combine(int n, int k) {
        backtrack(n, k, 1);
        return result;
    }
    public void backtrack(int n, int k, int startIndex) {
        if (path.size() == k) {
            result.add(new ArrayList(path));
            return;
        }
        for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) {
            path.add(i);
            backtrack(n, k, i+1);
            path.remove(path.size()-1);
        }
    }
}

时间复杂度: O(n * 2^n)
空间复杂度: O(n)

216. 组合总和 III

https://leetcode.cn/problems/combination-sum-iii/
在这里插入图片描述

class Solution {
    List<Integer> path = new ArrayList<>();
    List<List<Integer>> result = new ArrayList<>();
    public List<List<Integer>> combinationSum3(int k, int n) {
        backtrack(n, k, 1, 0);
        return result;
    }
    public void backtrack(int targetSum, int k, int startIndex, int sum) {
        if (path.size() == k) {
            if (sum == targetSum)
                result.add(new ArrayList(path));
            return;
        }
        for (int i = startIndex; i <= 9 - (k - path.size()) + 1; i++) {
            path.add(i);
            backtrack(targetSum, k, i+1, sum+i);
            path.remove(path.size() - 1);
        }
    }
}

17. 电话号码的字母组合

https://leetcode.cn/problems/letter-combinations-of-a-phone-number/
在这里插入图片描述

class Solution {
    String[] phone = new String[]{"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
    List<String> result = new ArrayList<>();
    StringBuilder path = new StringBuilder();
    public List<String> letterCombinations(String digits) {
        if (digits.length() == 0) return result;
        backtrack(digits, 0);
        return result;
    }
    public void backtrack(String digits, int index) {
        if (index == digits.length()) {
            result.add(path.toString());
            return;
        }
        String s = phone[digits.charAt(index) - '0'];
        for (int i = 0; i < s.length(); i++) {
            path.append(s.charAt(i));
            backtrack(digits, index+1);
            path.deleteCharAt(path.length()-1);
        }
    }
}

39. 组合总和

https://leetcode.cn/problems/combination-sum/
在这里插入图片描述

class Solution {
    List<Integer> path = new ArrayList<>();
    List<List<Integer>> result = new ArrayList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates);
        backtrack(candidates, target, 0, 0);
        return result;
    }
    public void backtrack(int[] candidates, int target, int sum, int startIndex) {
        if (sum > target) {
            return;
        }
        if (sum == target) {
            result.add(new ArrayList(path));
            return;
        }
        for (int i = startIndex; i < candidates.length && sum + candidates[i] <= target; i++) {
            path.add(candidates[i]);
            backtrack(candidates, target, sum+candidates[i], i);
            path.remove(path.size()-1);
        }
    }
}

40. 组合总和 II

https://leetcode.cn/problems/combination-sum-ii/
在这里插入图片描述

class Solution {
    List<Integer> path = new ArrayList<>();
    List<List<Integer>> result = new ArrayList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        backtrack(candidates, target, 0, 0);
        return result;
    }
    public void backtrack(int[] candidates, int target, int sum, int startIndex) {
        if (sum > target) {
            return;
        }
        if (sum == target) {
            result.add(new ArrayList(path));
            return;
        }
        for (int i = startIndex; i < candidates.length && sum + candidates[i] <= target; i++) {
            if (i > startIndex && candidates[i] == candidates[i-1]) continue;
            path.add(candidates[i]);
            backtrack(candidates, target, sum+candidates[i], i+1);
            path.remove(path.size()-1);
        }
    }
}

131. 分割回文串

https://leetcode.cn/problems/palindrome-partitioning/
在这里插入图片描述

class Solution {
    List<List<String>> result = new ArrayList<>();
    List<String> path = new ArrayList<>();
    public List<List<String>> partition(String s) {
        backtrack(s, 0);
        return result;
    }
    public void backtrack(String s, int startIndex) {
        if (startIndex == s.length()) {
            result.add(new ArrayList(path));
            return;
        }
        for (int i = startIndex; i < s.length(); i++) {
            if (isPalindrome(s, startIndex, i)) {
                path.add(s.substring(startIndex, i+1));    
            } else {
                continue;
            }
            backtrack(s, i+1);
            path.remove(path.size()-1);
        }
    }
    public boolean isPalindrome(String s, int start, int end) {
        for (int i = start, j = end; i < j; i++, j--) {
            if (s.charAt(i) != s.charAt(j)) {
                return false;
            }
        }
        return true;
    }
}

93. 复原 IP 地址

https://leetcode.cn/problems/restore-ip-addresses/
在这里插入图片描述

class Solution {
    List<String> result = new ArrayList<>();
    StringBuilder path = new StringBuilder();
    public List<String> restoreIpAddresses(String s) {
        backtrack(s, 0, 0);
        return result;
    }
    public void backtrack(String s, int startIndex, int pointNum) {
        if (startIndex >= s.length()) return;
        if (pointNum == 3) { // 判断最后一个分段是否合法
            if (isValid(s, startIndex, s.length()-1)) {
                path.append(s.substring(startIndex, s.length()));
                result.add(path.toString());
            }
            return;
        }
        for (int i = startIndex; i < s.length(); i++) {
            if (isValid(s, startIndex, i)) {
                path.append(s.substring(startIndex, i+1));
                if (pointNum < 3) 
                    path.append(".");
                backtrack(s, i+1, pointNum+1);
                path.delete(startIndex+pointNum, path.length()); // 删除最后的分段
            } else {
                break;
            }
        }
    }
    public boolean isValid(String s, int start, int end) {
        if (end + 1 - start > 3) return false; // 长度超过3 
        if (start != end && s.charAt(start) == '0') return false; // 有前导0
        if (Integer.valueOf(s.substring(start, end+1)) > 255) return false; // 值超过255
        return true;
    }
}

78. 子集

https://leetcode.cn/problems/subsets/
在这里插入图片描述

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> subsets(int[] nums) {
        backtrack(nums, 0);
        return result;
    }
    public void backtrack(int[] nums, int startIndex) {
        result.add(new ArrayList(path));
        if (startIndex == nums.length) {
            return;
        }
        for (int i = startIndex; i < nums.length; i++) {
            path.add(nums[i]);
            backtrack(nums, i+1);
            path.remove(path.size()-1);
        }
    }
}

90. 子集 II

https://leetcode.cn/problems/subsets-ii/
在这里插入图片描述

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        backtrack(nums, 0);
        return result;
    }
    public void backtrack(int[] nums, int startIndex) {
        result.add(new ArrayList(path));
        if (startIndex == nums.length) {
            return;
        }
        for (int i = startIndex; i < nums.length; i++) {
            if (i > startIndex && nums[i] == nums[i-1]) {
                continue;
            }
            path.add(nums[i]);
            backtrack(nums, i+1);
            path.remove(path.size()-1);
        }
    }
}

491. 递增子序列

https://leetcode.cn/problems/non-decreasing-subsequences/
在这里插入图片描述

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> findSubsequences(int[] nums) {
        backtrack(nums, 0);
        return result;
    }
    public void backtrack(int[] nums, int startIndex) {
        if (path.size() >= 2) {
            result.add(new ArrayList(path));
        }
        if (startIndex == nums.length) {
            return;
        }
        int[] used = new int[201]; // 记录本层使用过的结点,
        for (int i = startIndex; i < nums.length; i++) {
            if (path.size() > 0 && nums[i] < path.get(path.size()-1)) 
                continue;
            if (used[nums[i]+100] == 1) // 如果nums[i]在本层用过了,就跳过去
                continue;
            used[nums[i]+100] = 1;
            path.add(nums[i]);
            backtrack(nums, i+1);
            path.remove(path.size()-1);
        }
    }
}

46. 全排列

https://leetcode.cn/problems/permutations/
在这里插入图片描述

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> permute(int[] nums) {
        // 需要记录path路径上已经用过的元素
        int[] used = new int[nums.length]; 
        backtrack(nums, used);
        return result;
    }
    public void backtrack(int[] nums, int[] used) {
        if (path.size() == nums.length) {
            result.add(new ArrayList(path));
            return;
        }
        for (int i = 0; i < nums.length; i++) { // 对同一层的结点遍历
            if (used[i] == 1) {
                continue;
            }
            path.add(nums[i]);
            used[i] = 1;
            backtrack(nums, used); // 往下一层走
            path.remove(path.size()-1);
            used[i] = 0;
        }
    }
}

47. 全排列 II

https://leetcode.cn/problems/permutations-ii/
在这里插入图片描述

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> permuteUnique(int[] nums) {
        Arrays.sort(nums);
        // 需要记录path路径上已经用过的元素
        int[] used = new int[nums.length]; 
        backtrack(nums, used);
        return result;
    }
    public void backtrack(int[] nums, int[] used) {
        if (path.size() == nums.length) {
            result.add(new ArrayList(path));
            return;
        }
        for (int i = 0; i < nums.length; i++) { // 对同一层的结点遍历
            if (i > 0 && nums[i] == nums[i-1] && used[i-1] == 0)
                continue;
            if (used[i] == 1) {
                continue;
            }
            path.add(nums[i]);
            used[i] = 1;
            backtrack(nums, used); // 往下一层走
            path.remove(path.size()-1);
            used[i] = 0;
        }
    }
}

332. 重新安排行程(没做)

https://leetcode.cn/problems/reconstruct-itinerary/
在这里插入图片描述

51. N 皇后

https://leetcode.cn/problems/n-queens/
在这里插入图片描述

class Solution {
    List<List<String>> res = new ArrayList<>();
    public List<List<String>> solveNQueens(int n) {
        char[][] chessboard = new char[n][n]; // 定义一个棋盘
        for (char[] c : chessboard) { // 用.来填充棋盘
            Arrays.fill(c, '.');
        }
        backtrack(n, 0, chessboard); // 第二个参数表示棋盘的行,从第0行开始
        return res;
    }
    public void backtrack(int n, int row, char[][] chessboard) {
        if (row == n) { // 当“行”遍历完后,就表示找到了一个棋盘
            res.add(Arrays2List(chessboard));
            return;
        }
        for (int col = 0; col < n; col++) { // 遍历一行中的每一列
            if (isValid(row, col, n, chessboard)) { // 如果Q可以放在rowcol处,那就递归到下一行
                chessboard[row][col] = 'Q';
                backtrack(n, row+1, chessboard);
                chessboard[row][col] = '.';
            }
        }
    }
    public boolean isValid(int row, int col, int n, char[][] chessboard) {
        // 检查chessboard[row][col]处的皇后也没有和其他冲突
        // 列冲突
        for (int i = 0; i < row; i++) {
            if (chessboard[i][col] == 'Q') {
                return false;
            }
        }
        // 45度对角线
        for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
            if (chessboard[i][j] == 'Q') {
                return false;
            }
        }
        // 135度对角线
        for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
            if (chessboard[i][j] == 'Q') {
                return false;
            }
        }
        // 没有行冲突
        return true;
    }
    public List<String> Arrays2List(char[][] chessboard) {
        // 将char[][]数组转换成List<String>
        List<String> list = new ArrayList<>();
        for (char[] c : chessboard) {
            list.add(String.valueOf(c));
        }
        return list;
    }
}

37. 解数独

https://leetcode.cn/problems/sudoku-solver/
在这里插入图片描述

class Solution {
    public void solveSudoku(char[][] board) {
        backtrack(board);
    }
    public boolean backtrack(char[][] board) {
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] == '.') { // 可以填数
                    for (char val = '1'; val <= '9'; val++) {
                        // 遍历可以填的数
                        if (isValid(i, j, val, board)) {
                            // 如果可以填这个数
                            board[i][j] = val;
                            if (backtrack(board)) return true; // 递归去寻找,如果这个位置填入val后,递归得到的是true,说明这个位置就确定了,直接返回
                            board[i][j] = '.';
                        }
                    }
                    return false; // 遍历1-9都没有合法的,那就说明本次递归节点是无解的,返回false
                }
            }
        }
        return true;
    }
    public boolean isValid(int row, int col, char val, char[][] board) {
        // 判断在board[row][col]处填入val是否合法
        // 检查行、列、所在的3*3网格
        for (int i = 0; i < 9; i++) { // 检查列
            if (board[i][col] == val) {
                return false;
            }
        }
        for (int j = 0; j < 9; j++) { // 检查行
            if (board[row][j] == val) {
                return false;
            }
        }
        // 检查3*3网格
        int startRow = (row / 3) * 3;
        int startCol = (col / 3) * 3;
        for (int i = startRow; i < startRow + 3; i++) {
            for (int j = startCol; j < startCol + 3; j++) {
                if (board[i][j] == val) {
                    return false;
                }
            }
        }
        return true;
    }
}

贪心

动态规划

509. 斐波那契数

力扣题目链接

在这里插入图片描述

class Solution:
	def fib(self, n: int) -> int:
		if n < 2: return n
		a, b, c = 0, 1, 0
		for i in range(1, n):
			c = a + b
			a = b
			b = c
		return c
class Solution {
    public int fib(int n) {
        if (n < 2) return n;
        int a = 0, b = 1, ans = 0;
        for (int i = 1; i < n; i++) { // 执行n-1次for循环
            ans = a + b;
            a = b;
            b = ans;
        }
        return ans;
    }
}

70. 爬楼梯

力扣题目链接

在这里插入图片描述

class Solution {
    // 用变量记录代替数组
    public int climbStairs(int n) {
        /*
        f(i)表示爬n阶楼梯的方法数
        f(1) = 1, f(2) = 2
        */
        if(n <= 2) return n;
        int a = 1, b = 2, ans = 0;
        for(int i = 2; i < n; i++){ // for循环n-2次即可
            ans = a + b;
            a = b;
            b = ans;
        }
        return ans;
    }
}

746. 使用最小花费爬楼梯

力扣题目链接

在这里插入图片描述

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int len = cost.length;
        int[] dp = new int[len+1];
        dp[0] = 0;
        dp[1] = 0;
        for (int i = 2; i <= len; i++) {
            dp[i] = Math.min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2]);
        }
        return dp[len];
    }
}

62. 不同路径

力扣题目链接

在这里插入图片描述

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

63. 不同路径 II

力扣题目链接

在这里插入图片描述

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

343. 整数拆分

力扣题目链接

在这里插入图片描述
dp[i]:拆分数字i,可以得到的最大乘积为dp[i]
数字i可以拆分为1 * i, 2(i-1), 3(i-2), ..., (i/2) * (i-i/2) 再往后就重复了
所以定义变量j,j从1开始遍历到 i/2, i可以拆分为j * (i-j) ,因为j是从小到大的,所以i-j是从大到小,可以不断拆分
进一步,可以选择拆分i-j或者不拆分i-j

\[dp[i] = max(dp[i], max(j*(i-j), j*dp[i-j])) \]

class Solution {
    public int integerBreak(int n) {
        int[] dp = new int[n+1];
        for (int i = 2; i <= n; i++) { // 题目指定n>=2
            for (int j = 1; j <= i/2; j++) {
                dp[i] = Math.max(dp[i], Math.max(j * (i-j), j * dp[i-j]));
            }
        }
        return dp[n];
    }
}

96. 不同的二叉搜索树

力扣题目链接

在这里插入图片描述

dp[n]表示元素1到n构成的二叉搜索树的种数

举个例子分析:

dp[3]=元素1为头结点搜索树的数量+元素2为头结点搜索树的数量+元素3为头结点搜索树的数量

元素1为头结点搜索树的数量=右子树有2个元素的搜索树数量 * 左子树有0个元素的搜索树数量=dp[2] * dp[0]

元素2为头结点搜索树的数量=右子树有1个元素的搜索树数量 * 左子树有1个元素的搜索树数量=dp[1] * dp[1]

元素3为头结点搜索树的数量=右子树有0个元素的搜索树数量 * 左子树有2个元素的搜索树数量=dp[0] * dp[2]

所以dp[3] = dp[2] * dp[0] + dp[1] * dp[1] + dp[0] * dp[2]

所以

\[dp[n] = \sum_{i=0}^{n-1}dp[i]*dp[n-1-i] \]

初始化dp[0] = 1

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

01背包理论基础(一)

背包最大容量是4,物品重量分别为1、3、4,物品价值分别为15、20、30。问怎么装物品,才能获得最大价值?

动规五部曲:

  • 确定dp数组以及下标的含义:对于背包问题,有一种写法, 是使用二维数组,即dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
  • 确定递推公式:分别从不选i和选i来分析,可以得到递推公式为

\[dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]) \]

  • dp初试化:(1) 首先从dp[i][j]的定义出发,如果背包容量j为0的话,即dp[i][0],无论是选取哪些物品,背包价值总和一定为0,所以dp[i][0]=0。(2) 因为状态转移方程需要i-1来推导i,所以dp[0]必须初始化,d[0][j]表示存放编号0这个物品时,各个容量的背包所能存放的最大价值,所以当 j<weight[0] 时,背包容量放不下0号物品,dp[0][j]=0,当 j >= weight[0]时,dp[0][j] = value[0]
  • 确定遍历顺序:
  • 举例推导dp数组
public class Main {
    public static void main(String[] args) {
        int bagSize = 4; // 背包的承重
        int[] weight = {1, 3, 4}; // 物品重量
        int[] value = {15, 20, 30}; // 物品价值
        int n = weight.length; // 获取物品的个数
        int[][] dp = new int[n][bagSize+1];
        // 1、dp[i][j]表示从下标[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少
        // 2、dp[0][j] 就只能选第一个物品,所以最大不会超过value[0]
        for (int j = weight[0]; j <= bagSize; j++) {
            dp[0][j] = value[0];
        }
        // 3、确定递推公式,从不选i和选i来分析,dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i])
        for (int i = 1; i < n; i++) { // 遍历物品
            for (int j = 0; j <= bagSize; j++) { // 遍历背包容量
                if (j < weight[i]) { // 当前背包容量不足以放入物品i,那么只能不选i
                    dp[i][j] = dp[i-1][j];
                } else {
                    dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i]);
                }
            }
        }
        System.out.println(dp[n-1][bagSize]);
    }
}

01背包理论基础(二)

用一维dp数组来解决上一个问题

动规五部曲:

  • dp[j] 表示容量为j的背包所能盛放的物品最大价值。
  • 确定递推公式:

\[dp[j] = max(dp[j], dp[j-weight[i]]+value[i]) \]

  • dp初试化:全部初始化为0即可
  • 确定遍历顺序:正序遍历物品0、1、2号,逆序遍历背包容量,注意这里不能正序遍历
  • 举例推导dp数组:
public class Main{
	public static void main(String[] args){
		int bagSize = 4;
		int[] weight = {1, 3, 4};
		int[] value = {15, 20, 30};
		int[] dp = new int[bagSize+1];
		for (int i = 0; i < weight.length; i++){
			for (int j = bagSize; j >= weight[i]; j--){
				dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
			}
		}
		System.out.println(dp[bagSize]);
	}
}

416. 分割等和子集

力扣题目链接

在这里插入图片描述

dp[j]表示背包总容量是j时所能背的最大重量
通俗点说,例如数组[2,2,3,5],和为12,要想平均分成两份,每份就必须是6,也就是说给定一个背包容量为6,要从物品中选择,重量为[2,2,3,5],价值也为[2,2,3,5]
所以递推公式为

\[dp[j] = max(dp[j],dp[j-nums[i])+nums[i]) \]

按照01背包的方式处理完后,检查dp[6]是否等于6就可以判断能否二等分了

class Solution {
    public boolean canPartition(int[] nums) {
        int sum = 0;
        for (int num: nums) {
            sum += num;
        }
        if (sum % 2 == 1) return false;
        int target = sum / 2;
        int[] dp = new int[target+1];
        for (int i = 0; i < nums.length; i++) {
            for (int j = target; j >= nums[i]; j--) {
                dp[j] = Math.max(dp[j], dp[j-nums[i]] + nums[i]);
            }
        }
        return dp[target] == target;
    }
}

1049. 最后一块石头的重量II

力扣题目链接

在这里插入图片描述

class Solution {
    public int lastStoneWeightII(int[] stones) {
        int sum = 0;
        for (int num: stones) {
            sum += num;
        }
        int target = sum / 2;
        int[] dp = new int[target+1];
        for (int i = 0; i < stones.length; i++) {
            for (int j = target; j >= stones[i]; j--) {
                dp[j] = Math.max(dp[j], dp[j-stones[i]] + stones[i]);
            }
        }
        return sum - dp[target] - dp[target];
    }
}

本题其实和416. 分割等和子集 (opens new window)几乎是一样的,只是最后对dp[target]的处理方式不同。

494. 目标和

力扣题目链接

在这里插入图片描述

数组和为sum,假设加法的和为x,那么减法对应的和为sum-x,所以x-(sum-x)=target
即x=(target+sum)/2

dp[j] 表示:填满j(包括j)这么大容积的包,有dp[j]种方法

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for (int num: nums) {
            sum += num;
        }
        if (Math.abs(target) > sum) return 0;
        if ((target + sum) % 2 == 1) return 0;
        int bagSize  = (target + sum) / 2;
        int[] dp = new int[bagSize +1];
        dp[0] = 1; // 必须初始化为1,否则后面就全是0
        for (int i = 0; i < nums.length; i++) {
            for (int j = bagSize; j >= nums[i]; j--) {
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[bagSize];
    }
}

474. 一和零

力扣题目链接

在这里插入图片描述

class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
        # dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]
        # dp[i][j] = max(dp[i][j], dp[i-zeroNum][j-oneNum] + 1)
        dp = [[0] * (n+1) for _ in range(m+1)]
        # 外层for循环遍历物品
        for s in strs:
            oneNum = s.count('1') # 字符串中1的个数
            zeroNum = s.count('0') # 字符串中0的个数
            # 内层for循环遍历背包容量从后向前遍历
            for i in range(m, zeroNum-1, -1):
                for j in range(n, oneNum-1, -1):
                    dp[i][j] = max(dp[i][j], dp[i-zeroNum][j-oneNum] + 1)
        return dp[m][n]

完全背包理论基础

完全背包与01背包的区别在于,每种物品有无限件
在01背包中,需要从大到小遍历背包容量,因为每个物品只能放入一次
在完全背包中,每个物品可以无限次放入,所以需要从小到大遍历背包容量

518. 零钱兑换 II

力扣题目链接

在这里插入图片描述
dp[j]:凑成总金额j的货币组合数为dp[j]
物品不限个数,遍历背包容量时,从小到大
组合问题,先遍历物品,再遍历背包

class Solution {
    public int change(int amount, int[] coins) {
        int[] dp = new int[amount+1];
        dp[0] = 1;
        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j <= amount; j++) {
                dp[j] += dp[j-coins[i]];
            }
        }
        return dp[amount];
    }
}

377. 组合总和 Ⅳ

力扣题目链接
在这里插入图片描述
题目说是求组合,但其实是求排列,因为顺序不同被视作不同的组合,这不就是排列吗
dp[i]表示凑成正整数为j的排列个数
dp[i]+=dp[i-nums[j]]
dp[0]=1纯粹为了递推公式

注意:

class Solution {
    public int combinationSum4(int[] nums, int target) {
        int[] dp = new int[target+1];
        dp[0] = 1;
        for (int i = 1; i <= target; i++) {
            for (int j = 0; j < nums.length; j++) {
                if (i >= nums[j])
                    dp[i] += dp[i-nums[j]];
            }
        }
        return dp[target];
    }
}

爬楼梯进阶版

如果每一步可以选择爬一个台阶、两个台阶、三个台阶,.......,直到 m个台阶。问有多少种不同的方法可以爬到楼顶呢?
这是一个完全背包问题,这个题的代码和上一个完全一样,target就是楼梯阶数,nums就是[1,2,...,n]

322. 零钱兑换

力扣题目链接
在这里插入图片描述

class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount+1];
        for (int i = 0; i <= amount; i++) {
            dp[i] = Integer.MAX_VALUE;
        }
        dp[0] = 0;
        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j <= amount; j++) {
                if (dp[j-coins[i]] != Integer.MAX_VALUE)
                    dp[j] = Math.min(dp[j], dp[j-coins[i]] + 1);
            }
        }
        if (dp[amount] == Integer.MAX_VALUE) return -1;
        else return dp[amount];
    }
}

279. 完全平方数

力扣题目链接
在这里插入图片描述

class Solution {
    public int numSquares(int n) {
        // 完全背包 遍历背包时由小到大,组合问题,先物品后背包
        int[] dp = new int[n+1];
        for (int i = 0; i <= n; i++) {
            dp[i] = Integer.MAX_VALUE;
        }
        dp[0] = 0;
        for (int i = 1; i * i <= n; i++) {
            for (int j = i*i; j <= n; j++) {
                dp[j] = Math.min(dp[j], dp[j-i*i] + 1);
            }
        }
        return dp[n];
    }
}

139. 单词拆分

力扣题目链接
在这里插入图片描述

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        /*
        s是背包,wordDict是物品
        dp[i]:字符串长度为i的时候,dp[i]=true,表示可以拆分为1个或者多个在字典中出现的单词
        这是排列问题,外层for遍历背包,内层for遍历物品
        如果确定dp[j] 是true,且 [j, i] 这个区间的子串出现在字典里,那么dp[i]一定是true。(j < i )。
        */
        HashSet<String> wordDictSet = new HashSet(wordDict); // 转成哈希表来查找更快
        boolean[] dp = new boolean[s.length()+1];
        dp[0] = true;
        for (int i = 1; i <= s.length(); i++) {
            for (int j = 0; j < i; j++) {
                if (dp[j] && wordDictSet.contains(s.substring(j,i))) {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.length()];
    }
}

背包问题总结

01背包问题:二维dp,先遍历物品或者先遍历背包都可以,遍历背包时没有顺序要求
一维dp,必须先遍历物品后遍历背包,遍历背包时要按照从大到小
完全背包问题:一维dp没有先后遍历顺序的要求,但是遍历背包要从小到大
组合问题:组合问题,先遍历物品,再遍历背包;
排列问题:先遍历背包,再遍历物品

动态规划五部曲:

  • 确定dp数组(dp table)以及下标的含义
  • 确定递推公式
  • dp数组如何初始化
  • 确定遍历顺序
  • 举例推导dp数组

递推公式
问能否能装满背包(或者最多装多少):dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]),对应题目如下:
动态规划:416.分割等和子集
动态规划:1049.最后一块石头的重量 II
问装满背包有几种方法:dp[j] += dp[j - nums[i]] ,对应题目如下:
动态规划:494.目标和
动态规划:518. 零钱兑换 II
动态规划:377.组合总和Ⅳ
动态规划:70. 爬楼梯进阶版(完全背包)
问背包装满最大价值:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]),对应题目如下:
动态规划:474.一和零
问装满背包所有物品的最小个数:dp[j] = min(dp[j], dp[j - coins[i]] + 1),对应题目如下:
动态规划:322.零钱兑换
动态规划:279.完全平方数

遍历顺序
01背包
二维dp数组01背包先遍历物品还是先遍历背包都是可以的,且第二层for循环是从小到大遍历。
一维dp数组01背包只能先遍历物品再遍历背包容量,且第二层for循环是从大到小遍历。

完全背包
纯完全背包的一维dp数组实现,先遍历物品还是先遍历背包都是可以的,且第二层for循环是从小到大遍历。
如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品。

198. 打家劫舍

题目链接
在这里插入图片描述

class Solution {
    public int rob(int[] nums) {
        // dp[i]表示包括i在内之前的房屋最大偷窃金额
        // dp[i] = max(dp[i-2] + nums[i], dp[i-1])
        int n = nums.length;
        if (n == 1) return nums[0];
        int[] dp = new int[n];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[0], nums[1]);
        for (int i = 2; i < n; i++) {
            dp[i] = Math.max(dp[i-2] + nums[i], dp[i-1]);
        }
        return dp[n-1];
    }
}

213. 打家劫舍 II

力扣题目链接
在这里插入图片描述
相比于上一题,这一题是一个环

class Solution {
    public int rob(int[] nums) {
        if (nums.length == 1) return nums[0];
        int results1 = robRange(nums, 0, nums.length-2); // 去掉尾元素
        int results2 = robRange(nums, 1, nums.length-1); // 去掉首元素
        return Math.max(results1, results2);
    }
    public int robRange(int[] nums, int start, int end) {
        if (start == end) return nums[start];
        int[] dp = new int[nums.length];
        dp[start] = nums[start];
        dp[start+1] = Math.max(nums[start], nums[start+1]);
        for (int i = start+2; i <= end; i++) {
            dp[i] = Math.max(dp[i-2]+nums[i], dp[i-1]);
        }
        return dp[end];
    }
}

337. 打家劫舍 III

https://leetcode.cn/problems/house-robber-iii/
在这里插入图片描述

class Solution {
    public int rob(TreeNode root) {
        int[] res = robTree(root);
        return Math.max(res[0], res[1]);
    }
    public int[] robTree(TreeNode cur) {
        int[] res = new int[2];
        if (cur == null) return res;
        // 返回当前节点不偷或偷的最大盗取金额,返回结果为{不偷,偷}
        int[] left = robTree(cur.left);
        int[] right = robTree(cur.right);
        // 不偷当前节点,
        res[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
        // 偷当前节点,左右就不能偷
        res[1] = cur.val + left[0] + right[0];
        return res;
    }
}

121. 买卖股票的最佳时机

https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/
在这里插入图片描述

class Solution {
    public int maxProfit(int[] prices) {
        int min = Integer.MAX_VALUE;
        int max = 0;
        for (int i = 0; i < prices.length; i++) {
            if (prices[i] < min) {
                min = prices[i];
            } else if (prices[i] - min > max) {
                max = prices[i] - min;
            }
        }
        return max;
    }
}

122. 买卖股票的最佳时机 II

https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/
在这里插入图片描述

class Solution {
    public int maxProfit(int[] prices) {
		// 贪心
        int res = 0;
        for (int i = 1; i < prices.length; i++) {
            // 计算当天与前一天的差值,得到每天的利润,将利润大于0的加起来就是最终结果
            res += Math.max(prices[i] - prices[i-1], 0);
        }
        return res;
    }
}

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

https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/
在这里插入图片描述

class Solution {
    public int maxProfit(int[] prices) {
        /*
        一天一共就有五个状态,
        0 没有操作 (其实我们也可以不设置这个状态)
        1 第一次持有股票
        2 第一次不持有股票
        3 第二次持有股票
        4 第二次不持有股票
        dp[i][j]中 i表示第i天,j为 [0 - 4] 五个状态,dp[i][j]表示第i天状态j手头的现金。
        dp[i][0] = dp[i-1][0]
        dp[i][1] = Math.max(dp[i-1][1], dp[i][0] - prices[i]) 延续前一天的状态,或者第i天买入 再减去股票钱 求max
        dp[i][2] = Math.max(dp[i-1][2], dp[i][1] + prices[i]) 延续前一天的状态,或者第i天卖出 再加上股票钱 求max
        dp[i][3] = Math.max(dp[i-1][3], dp[i][2] - prices[i]) 延续前一天的状态,或者第i天卖出 再加上股票钱 求max
        dp[i][4] = Math.max(dp[i-1][4], dp[i][3] + prices[i]) 延续前一天的状态,或者第i天卖出 再加上股票钱 求max
        */
        int[][] dp = new int[prices.length][5];
        dp[0][1] = -prices[0];
        dp[0][3] = -prices[0];
        for (int i = 1; i < prices.length; i++) {
            dp[i][0] = dp[i-1][0];
            dp[i][1] = Math.max(dp[i-1][1], dp[i][0] - prices[i]);
            dp[i][2] = Math.max(dp[i-1][2], dp[i][1] + prices[i]);
            dp[i][3] = Math.max(dp[i-1][3], dp[i][2] - prices[i]);
            dp[i][4] = Math.max(dp[i-1][4], dp[i][3] + prices[i]);
        }
        return dp[prices.length-1][4];
    }
}

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

https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iv/
在这里插入图片描述

class Solution {
    public int maxProfit(int k, int[] prices) {
        /*
        0 不操作
        1 第一次买入
        2 第一次卖出
        3 第二次买入
        4 第二次卖出
        。。。
        */
        int[][] dp = new int[prices.length][2 * k + 1];
        // j为奇数时,dp[0][j] = -prices[0]
        for (int j = 1; j < 2 * k + 1; j += 2) {
            dp[0][j] = -prices[0];
        }
        for (int i = 1; i < prices.length; i++) {
            for (int j = 1; j < 2 * k + 1; j+=2) {
                dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-1] - prices[i]); // 对应的是买入
                dp[i][j+1] = Math.max(dp[i-1][j+1], dp[i-1][j] + prices[i]); // 对应的是卖出
            }
        }
        return dp[prices.length-1][2*k];
    }
}

309. 买卖股票的最佳时机含冷冻期(没做)

https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-cooldown/

714. 买卖股票的最佳时机含手续费(没做)

https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/

300. 最长递增子序列

https://leetcode.cn/problems/longest-increasing-subsequence/
在这里插入图片描述

class Solution {
    public int lengthOfLIS(int[] nums) {
        // dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度
        // 位置i处的最长递增子序列就是求j从0到i-1各个位置的最长递增子序列+1的最大值
        if (nums.length <= 1)
            return nums.length;
        int res = 0;
        int[] dp = new int[nums.length];
        Arrays.fill(dp, 1);
        for (int i = 1; i < nums.length; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[i] > nums[j]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            res = Math.max(res, dp[i]);
        }
        return res;
    }
}

674. 最长连续递增序列

https://leetcode.cn/problems/longest-continuous-increasing-subsequence/
在这里插入图片描述

class Solution {
    public int findLengthOfLCIS(int[] nums) {
        int res = 1;
        int cur_len = 1;
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] > nums[i-1]) {
                cur_len += 1;
                res = Math.max(res, cur_len);
            } else {
                cur_len = 1;
            }
        }
        return res;
    }
}

718. 最长重复子数组

https://leetcode.cn/problems/maximum-length-of-repeated-subarray/
在这里插入图片描述

class Solution {
    public int findLength(int[] nums1, int[] nums2) {
        /*
        dp[i][j] 表示以 nums1[i-1]结尾 nums2[j-1]结尾的公共最长子数组的长度
        */
        int res = 0;
        int[][] dp = new int[nums1.length+1][nums2.length+1];
        for (int i = 1; i < nums1.length+1; i++) {
            for (int j = 1; j < nums2.length+1; j++) {
                if (nums1[i-1] == nums2[j-1]) {
                    dp[i][j] = dp[i-1][j-1] + 1;
                }
                res = Math.max(res, dp[i][j]);
            }
        }
        return res;
    }
}

1143. 最长公共子序列

https://leetcode.cn/problems/longest-common-subsequence/
在这里插入图片描述

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int[][] dp = new int[text1.length()+1][text2.length()+1];
        for (int i = 1; i <= text1.length(); i++) {
            for (int j = 1; j <= text2.length(); j++) {
                if (text1.charAt(i-1) == text2.charAt(j-1)) 
                    dp[i][j] = dp[i-1][j-1] + 1;
                else
                    dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
            }
        }
        return dp[text1.length()][text2.length()];
    }
}

53. 最大子数组和

https://leetcode.cn/problems/maximum-subarray/
在这里插入图片描述

class Solution {
    public int maxSubArray(int[] arr) {
        int[] dp = new int[arr.length];
        dp[0] = arr[0];
        int res = dp[0];
        for (int i = 1; i < arr.length; i++) {
            dp[i] = Math.max(dp[i-1] + arr[i], arr[i]);
            res = Math.max(res, dp[i]);
        }
        return res;
    }
}

392. 判断子序列

https://leetcode.cn/problems/is-subsequence/
在这里插入图片描述

class Solution {
    public boolean isSubsequence(String s, String t) {
        // dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]。
        int[][] dp = new int[s.length()+1][t.length()+1];
        for (int i = 1; i < s.length() + 1; i++) {
            for (int j = 1; j < t.length() + 1; j++) {
                if (s.charAt(i-1) == t.charAt(j-1))
                    dp[i][j] = dp[i-1][j-1] + 1;
                else
                    dp[i][j] = dp[i][j-1];
            }
        }
        if (dp[s.length()][t.length()] == s.length()) return true;
        return false;
    }
}

115. 不同的子序列

https://leetcode.cn/problems/distinct-subsequences/
在这里插入图片描述

class Solution {
    public int numDistinct(String s, String t) {
        // 可以理解为删除s中的元素,有多少种方案可以变成t
        int[][] dp = new int[s.length()+1][t.length()+1];
        for (int i = 0; i <= s.length(); i++) dp[i][0] = 1;
        for (int j = 1; j <= t.length(); j++) dp[0][j] = 0;
        for (int i = 1; i <= s.length(); i++) {
            for (int j = 1; j <= t.length(); j++) {
                if (s.charAt(i-1) == t.charAt(j-1)) {
                    dp[i][j] = dp[i-1][j-1] + dp[i-1][j];
                } else {
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
        return dp[s.length()][t.length()];
    }
}

583. 两个字符串的删除操作

https://leetcode.cn/problems/delete-operation-for-two-strings/
在这里插入图片描述

class Solution {
    public int minDistance(String word1, String word2) {
        // dp[i][j]:以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最少次数
        int[][] dp = new int[word1.length()+1][word2.length()+1];
        // 初始化
        for (int i = 1; i <= word1.length(); i++) dp[i][0] = i;
        for (int j = 1; j <= word2.length(); j++) dp[0][j] = j;
        for (int i = 1; i <= word1.length(); i++) {
            for (int j = 1; j <= word2.length(); j++) {
                if (word1.charAt(i-1) == word2.charAt(j-1)) { // 相等就不用删
                    dp[i][j] = dp[i-1][j-1];
                } else {
                    // 有2种情况
                    // 删除word1的 dp[i][j] = dp[i-1][j] + 1;
                    // 删除word2的 dp[i][j] = dp[i][j-1] + 1;
                    dp[i][j] = Math.min(dp[i-1][j] + 1, dp[i][j-1] + 1);
                }
            }
        }
        return dp[word1.length()][word2.length()];
    }
}

72. 编辑距离

https://leetcode.cn/problems/edit-distance/
在这里插入图片描述

class Solution {
    public int minDistance(String word1, String word2) {
        /*
        dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]
        */
        int[][] dp = new int[word1.length()+1][word2.length()+1];
        for (int i = 0; i <= word1.length(); i++) dp[i][0] = i;
        for (int j = 0; j <= word2.length(); j++) dp[0][j] = j;
        for (int i = 1; i <= word1.length(); i++) {
            for (int j = 1; j <= word2.length(); j++) {
                if (word1.charAt(i-1) == word2.charAt(j-1)) {
                    dp[i][j] = dp[i-1][j-1];
                } else {
                    dp[i][j] = Math.min(Math.min(dp[i-1][j-1], dp[i-1][j]), dp[i][j-1]) + 1;
                    // word1删除一个元素, dp[i][j] = dp[i - 1][j] + 1;
                    // word1添加一个元素(word2删除一个元素), dp[i][j] = dp[i][j - 1] + 1;
                    // word1替换一个元素, dp[i][j] = dp[i - 1][j - 1] + 1;
                }
            }
        }
        return dp[word1.length()][word2.length()];
    }
}

647. 回文子串

https://leetcode.cn/problems/palindromic-substrings/
在这里插入图片描述

class Solution {
    public int countSubstrings(String s) {
        int res = 0;
        // 中心扩展法
        for (int i = 0; i < s.length(); i++) {
            res += expand(s, i, i); // 以i为中心
            res += expand(s, i, i+1); // 以i+1为中心
        }
        return res;
    }
    public int expand(String s, int left, int right) {
        int res = 0;
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            left--;
            right++;
            res++;
        }
        return res;
    }
}

516. 最长回文子序列

https://leetcode.cn/problems/longest-palindromic-subsequence/
在这里插入图片描述

class Solution {
    public int longestPalindromeSubseq(String s) {
        // dp[i][j]:字符串s在[i, j]范围内最长的回文子序列的长度
        // if(s[i] == s[j]) dp[i][j] = dp[i+1][j-1] + 2
        // else dp[i][j] = max(dp[i+1][j], dp[i][j-1])
        // i要依赖i+1,j要依赖j-1,所以i从右往左遍历,因为j要在i的右边,所以j从i+1开始往右遍历
        int[][] dp = new int[s.length()][s.length()];
        // 对角线初始化为1
        for (int k = 0; k < s.length(); k++) {
            dp[k][k] = 1;
        }
        for (int i = s.length() - 1; i >= 0; i--) {
            for (int j = i + 1; j < s.length(); j++) {
                if (s.charAt(i) == s.charAt(j)) {
                    dp[i][j] = dp[i+1][j-1] + 2;
                } else {
                    dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]);
                }
            }
        }
        return dp[0][s.length()-1];
    }
}

单调栈

739. 每日温度

https://leetcode.cn/problems/daily-temperatures/
在这里插入图片描述

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int n = temperatures.length;
        int[] res = new int[n];
        Deque<Integer> deque = new LinkedList<>(); // 存放下标
        deque.push(0); // 先把第一个元素下标放进去
        for (int i = 1; i < n; i++) {
            while (!deque.isEmpty() && temperatures[i] > temperatures[deque.peek()]) {
                res[deque.peek()] = i - deque.peek();
                deque.pop();
            }
            deque.push(i);
            
        }
        return res;
    }
}

496. 下一个更大元素 I

https://leetcode.cn/problems/next-greater-element-i/

在这里插入图片描述

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums1.length; i++) {
            map.put(nums1[i], i);
        }
        int[] res = new int[nums1.length];
        Arrays.fill(res, -1);
        Deque<Integer> deque = new LinkedList<>();
        for (int i = 0; i < nums2.length; i++) {
            while (!deque.isEmpty() && nums2[i] > nums2[deque.peek()]) {
                int pre = nums2[deque.pop()];
                if (map.containsKey(pre)) {
                    res[map.get(pre)] = nums2[i];
                }
            }
            deque.push(i);
        }
        return res;
    }
}

503. 下一个更大元素 II

https://leetcode.cn/problems/next-greater-element-ii/
在这里插入图片描述

class Solution {
    public int[] nextGreaterElements(int[] nums) {
        Deque<Integer> deque = new LinkedList<>();
        int n = nums.length;
        int[] res = new int[n];
        Arrays.fill(res, -1);
        for (int i = 0; i < n * 2; i++) {
            while (!deque.isEmpty() && nums[i % n] > nums[deque.peek()]) {
                res[deque.pop()] = nums[i % n];
            }
            deque.push(i % n);
        }
        return res;
    }
}

42. 接雨水

https://leetcode.cn/problems/trapping-rain-water/description/
在这里插入图片描述

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

84. 柱状图中最大的矩形

https://leetcode.cn/problems/largest-rectangle-in-histogram/description
在这里插入图片描述

class Solution {
    public int largestRectangleArea(int[] heights) {
        // 数组扩容,在height前后各加一个0
        int n = heights.length;
        int[] newHeights = new int[n + 2];
        newHeights[0] = 0;
        newHeights[n + 1] = 0;
        for (int i = 1; i < n + 1; i++) {
            newHeights[i] = heights[i - 1];
        }
        int res = 0;
        Deque<Integer> deque = new LinkedList<>();
        for (int i = 0; i < newHeights.length; i++) {
            while (!deque.isEmpty() && newHeights[i] < newHeights[deque.peek()]) {
                int mid = deque.pop();
                int w = i - deque.peek() - 1;
                int h = newHeights[mid];
                res = Math.max(res, h * w);
            }
            deque.push(i);
        }
        return res;
    }
}

图论

posted @ 2023-10-23 21:00  若乔  阅读(112)  评论(0)    收藏  举报