力扣Hot100刷题笔记

哈希

1 两数之和

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

49 字母异位词分组

Map+排序

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> map = new HashMap<>();
        for (String str : strs) {
            char[] array = str.toCharArray();
            Arrays.sort(array);
            // 注意这里转String的方法,不能用toString
            String key = new String(array);
            List<String> list = map.getOrDefault(key, new ArrayList<>());
            list.add(str);
            map.put(key, list);
        }
        return new ArrayList<List<String>>(map.values());
    }
}

128 最长连续序列

class Solution {
    public int longestConsecutive(int[] nums) {
        Set<Integer> set = new HashSet<>();
        for (int num : nums) {
            set.add(num);
        }
        int res = 0;
        for (int num : nums) {
            // set中不包含num - 1时统计序列长度
            if (!set.contains(num - 1)) {
                int curNum = num;
                int curRes = 1;
                while (set.contains(curNum + 1)) {
                    curRes++;
                    curNum++;
                }
                res = Math.max(res, curRes);
            }
        }
        return res;
    }
}

双指针

283 移动零

class Solution {
    public void moveZeroes(int[] nums) {
        int j = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != 0) {
                int tmp = nums[i];
                nums[i] = nums[j];
                nums[j++] = tmp;
            }
        }
    }
}

11 盛最多水的容器

class Solution {
    public int maxArea(int[] height) {
        int res = 0;
        int left = 0, right = height.length - 1;
        while (left <= right) {
            int weight = right - left;
            int curRes = 0;
            if (height[right] > height[left]) {
                curRes = height[left] * weight;
                left++;
            } else {
                curRes = height[right] * weight;
                right--;
            }
            res = Math.max(res, curRes);
        }
        return res;
    }
}

15 三数之和

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        int n = nums.length;
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();

        for (int i = 0; i < n; i++) {
            // 保证i没被考虑过,只使用每个值的第一个位置
            if (i > 0 && nums[i] == nums[i - 1]) continue;
            // 优化,最小的三个数大于0则停止循环
            if (nums[i] + nums[i + 1] + nums[i + 2] > 0) break;
            // 优化,与最后两个数相加小于0则可以进入下一个循环
            if (nums[i] + nums[n - 1] + nums[n - 2] < 0) continue;
            int l = i + 1, r = n - 1;
            while (l < r) {
                int sum = nums[i] + nums[l] + nums[r];
                if (sum == 0) {
                    List<Integer> list = Arrays.asList(nums[i], nums[l], nums[r]);
                    res.add(list);
                    while (l < r && nums[l] == nums[l + 1]) ++l; // 跳过重复的元素
                    while (l < r && nums[r] == nums[r - 1]) --r; // 跳过重复的元素
                    ++l; --r;
                } else if (sum < 0) {
                    l++;
                } else {
                    r--;
                }
            }
        }
        return res;
    }
}

42 接雨水

盛最多水的容器 接雨水_哔哩哔哩_bilibili

前缀最大值数组+后缀最大值数组

class Solution {
    public int trap(int[] height) {
        int n = height.length;
        int[] pre_arr = new int[n];
        int[] suf_arr = new int[n];
        pre_arr[0] = height[0];
        suf_arr[n - 1] = height[n - 1];
        int res = 0;

        for (int i = 1; i < n; i++) {
            pre_arr[i] = Math.max(height[i], pre_arr[i - 1]);
        }

        for (int j = n - 2; j >= 0; j--) {
            suf_arr[j] = Math.max(height[j], suf_arr[j + 1]);
        }

        for (int i = 0; i < n; i++) {
            res += Math.min(pre_arr[i], suf_arr[i]) - height[i];
        }
        return res;
    }
}

空间优化

class Solution {
    public int trap(int[] height) {
        int n = height.length;
        int left = 0, right = n - 1;
        int pre_max = 0, suf_max = 0;
        int res = 0;
        while (left < right) {
            pre_max = Math.max(pre_max, height[left]);
            suf_max = Math.max(suf_max, height[right]);
            if (pre_max < suf_max) {
                res += pre_max - height[left];
                left++;
            } else {
                res += suf_max - height[right];
                right--;
            }
        }
        return res;
    }
}

滑动窗口

补充

209 长度最小的子数组

遍历右端点,缩减左端点

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int n = nums.length;
        int sum = 0;
        int left = 0;
        int res = n + 1;
        // 遍历右端点
        for (int i = 0; i < n; i++) {
            sum += nums[i];
            while (sum - nums[left] >= target) {
                sum -= nums[left++];
            }
            // 这里加一层判断的原因是刚开始遍历时可能sum
            if (sum >= target) {
                res = Math.min(res, i - left + 1);
            }
        }
        return res > n ? 0 : res;
    }
}
713 乘积小于K的数组

遍历右端点,缩减左端点

class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        if (k <= 1) return 0;
        int left = 0, n = nums.length;
        int res = 0, prod = 1;
        for (int i = 0; i < n; i++) {
            prod *= nums[i];
            while (prod >= k) {
                prod /= nums[left];
                left++;
            }
            // 以i为右端点的子数组数目就是长度
            res += i - left + 1;
        }
        return res;
    }
}

3 无重复字符的最长子串

滑动窗口【基础算法精讲 03】_哔哩哔哩_bilibili

class Solution {
    public int lengthOfLongestSubstring(String s) {
        char[] arr = s.toCharArray();
        int n = s.length();
        int res = 0;
        int left = 0;
        // map记录字符出现的次数
        Map<Character, Integer> map = new HashMap<>();
        for (int i = 0; i < n; i++) {
            map.put(arr[i], map.getOrDefault(arr[i], 0) + 1);
            while (map.get(arr[i]) > 1) {
                map.put(arr[left], map.get(arr[left]) - 1);
                left++;
            }
            res = Math.max(res, i - left + 1);
        }
        return res;
    }
}

// 优化 
class Solution {
    public int lengthOfLongestSubstring(String s) {
        char[] arr = s.toCharArray();
        int n = s.length();
        int res = 0;
        int left = 0;
        // map记录字符最后出现的位置
        Map<Character, Integer> map = new HashMap<>();
        for (int i = 0; i < n; i++) {
            if (map.containsKey(arr[i])) {
                left = Math.max(left, map.get(arr[i]) + 1);
            }
            map.put(arr[i], i);
            res = Math.max(res, i - left + 1);
        }
        return res;
    }
}

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

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int sLen = s.length(), pLen = p.length();
        if (sLen < pLen) return new ArrayList<Integer>();

        List<Integer> res = new ArrayList<>();
        int[] sCount = new int[26];
        int[] pCount = new int[26];
        for (int i = 0; i < pLen; i++) {
            sCount[s.charAt(i) - 'a']++;
            pCount[p.charAt(i) - 'a']++;
        }

        if (Arrays.equals(sCount, pCount)) res.add(0);

        // 遍历右端点
        for (int i = pLen; i < sLen; i++) {
            sCount[s.charAt(i) - 'a']++;
            // 移动左端点
            sCount[s.charAt(i - pLen) - 'a']--;
            if (Arrays.equals(pCount, sCount)) {
                res.add(i - pLen + 1);
            }
        }

        return res;
    }
}

子串

560 和为K的子数组

前缀和

class Solution {
    public int subarraySum(int[] nums, int k) {
        int res = 0;
        int pre = 0;
        // map中记录前缀和的个数
        Map<Integer, Integer> map = new HashMap<>();
        // 初始化map,存在前缀和刚好为k的情况
        map.put(0, 1);
        for (int num : nums) {
            pre += num;
            if (map.containsKey(pre - k)) {
                // 结果加上前缀和为pre-k的个数
                res += map.get(pre - k);
            }
            map.put(pre, map.getOrDefault(pre, 0) + 1);
        }
        return res;
    }
}

239 滑动窗口最大值

单调队列 滑动窗口最大值【基础算法精讲 27】_哔哩哔哩_bilibili

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        Deque<Integer> deq = new LinkedList<>();
        int n = nums.length;
        int[] res = new int[n - k + 1];
        for (int i = 0; i < n; i++) {
            // 1 入
            while (!deq.isEmpty() && nums[deq.getLast()] <= nums[i]) {
                deq.removeLast();
            }
            deq.addLast(i);
            // 2 出
            if (i - deq.getFirst() + 1 > k) { // 队首已经离开窗口
                deq.removeFirst();
            }
            // 3 添加结果
            if (i + 1 >= k) {
                res[i - k + 1] = nums[deq.getFirst()];
            }
        }
        return res;
    }
}

76 最小覆盖子串

题目:给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""

class Solution {
    public String minWindow(String S, String T) {
        char[] s = S.toCharArray();
        char[] t = T.toCharArray();
        int m = s.length;
        int ansLeft = -1;
        int ansRight = m;
        int left = 0;
        int[] cntS = new int[128]; // s 子串字母的出现次数
        int[] cntT = new int[128]; // t 中字母的出现次数
        for (char c : t) {
            cntT[c]++;
        }
        for (int right = 0; right < m; right++) { // 移动子串右端点
            cntS[s[right]]++; // 右端点字母移入子串
            while (isCovered(cntS, cntT)) { // 涵盖
                if (right - left < ansRight - ansLeft) { // 找到更短的子串
                    ansLeft = left; // 记录此时的左右端点
                    ansRight = right;
                }
                cntS[s[left++]]--; // 左端点字母移出子串
            }
        }
        return ansLeft < 0 ? "" : S.substring(ansLeft, ansRight + 1);
    }

    private boolean isCovered(int[] cntS, int[] cntT) {
        for (int i = 'A'; i <= 'Z'; i++) {
            if (cntS[i] < cntT[i]) {
                return false;
            }
        }
        for (int i = 'a'; i <= 'z'; i++) {
            if (cntS[i] < cntT[i]) {
                return false;
            }
        }
        return true;
    }
}

普通数组

53 最大子数组和

动态规划,记录当前最大和

class Solution {
    public int maxSubArray(int[] nums) {
        int pre = 0, res = nums[0];
        for (int num : nums) {
            pre = Math.max(pre + num, num);
            res = Math.max(res, pre);
        }
        return res;
    }
}

56 合并区间

https://leetcode.cn/problems/merge-intervals/description/

思路:先排序,再合并

class Solution {
    public int[][] merge(int[][] intervals) {
        if (intervals.length == 0) {
            return new int[0][2];
        }

        Arrays.sort(intervals, new Comparator<int[]>() {
            public int compare(int[] interval1, int[] interval2) {
                return interval1[0] - interval2[0];
            }
        });

        List<int[]> merged = new ArrayList<>();
        for (int i = 0; i < intervals.length; i++) {
            int L = intervals[i][0], R = intervals[i][1];
            if (merged.size() == 0 || merged.get(merged.size() - 1)[1] < L) {
                merged.add(new int[]{L, R});
            } else {
                merged.get(merged.size() - 1)[1] = Math.max(merged.get(merged.size() - 1)[1], R);
            }
        }
        return merged.toArray(new int[merged.size()][]);
    }
}

189 轮转数组

3次翻转

class Solution {
    public void rotate(int[] nums, int k) {
        k = k % nums.length;
        reverse(nums, 0, nums.length - k - 1);
        reverse(nums, nums.length - k, nums.length - 1);
        reverse(nums, 0, nums.length - 1);
    }

    public void reverse(int[] nums, int l, int r) {
        while (l < r) {
            int tmp = nums[l];
            nums[l++] = nums[r];
            nums[r--] = tmp;
        }
    }
}

238 除自身以外数组的乘积

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        int[] res = new int[n];
        // 计算左侧乘积
        res[0] = 1;
        for (int i = 1; i < n; i++) {
            res[i] = res[i - 1] * nums[i - 1];
        }
        // 合并右侧乘积
        int R = 1;
        for (int i = n - 1; i >= 0; i--) {
            res[i] = res[i] * R;
            R *= nums[i];
        }
        return res;
    }
}

41 缺失的第一个正数

class Solution {
    public int firstMissingPositive(int[] nums) {
        int n = nums.length;

        // 把小于0的数变为n+1
        for (int i = 0; i < n; i++) {
            if (nums[i] <= 0) {
                nums[i] = n + 1;
            }
        }

        // 原地哈希,把不大于n的数对应位置置为负
        for (int i = 0; i < n; i++) {
            int x = Math.abs(nums[i]);
            if (x <= n) {
                nums[x - 1] = -Math.abs(nums[x - 1]);
            }
        }

        // 大于0的位置对应缺少的正数
        for (int i = 0; i < n; i++) {
            if (nums[i] > 0) {
                return i + 1;
            }
        }

        return n + 1;
    }
}

矩阵

73 矩阵置零

class Solution {
    public void setZeroes(int[][] matrix) {
        int m = matrix.length;
        int n = matrix[0].length;
        boolean flagRow0 = false, flagCol0 = false;

        for (int i = 0; i < m; i++) {
            if (matrix[i][0] == 0) {
                flagCol0 = true;
                break;
            }
        }

        for (int j = 0; j < n; j++) {
            if (matrix[0][j] == 0) {
                flagRow0 = true;
                break;
            }
        }

        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (matrix[i][j] == 0) {
                    matrix[i][0] = 0;
                    matrix[0][j] = 0;
                }
            }
        }

        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (matrix[i][0] == 0 || matrix[0][j] == 0) {
                    matrix[i][j] = 0;
                }
            }
        }

        if (flagCol0 == true) {
            for (int i = 0; i < m; i++) {
                matrix[i][0] = 0;
            }
        }

        if (flagRow0 == true) {
            for (int j = 0; j < n; j++) {
                matrix[0][j] = 0;
            }
        }
    }
}

54 螺旋矩阵

class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> res = new ArrayList<>();
        int u = 0, d = matrix.length - 1, l = 0, r = matrix[0].length - 1;
        while (true) {
            for (int i = l; i <= r; i++) res.add(matrix[u][i]);
            if (++u > d) break;
            for (int i = u; i <= d; i++) res.add(matrix[i][r]);
            if (--r < l) break;
            for (int i = r; i >= l; i--) res.add(matrix[d][i]);
            if (--d < u) break;
            for (int i = d; i >= u; i--) res.add(matrix[i][l]);
            if (++l > r) break;
        }
        return res;
    }
}

48 旋转图像

class Solution {
    public void rotate(int[][] matrix) {
        int n = matrix.length;
        // 水平翻转
        for (int i = 0; i < n / 2; i++) {
            for (int j = 0; j < n; j++) {
                int tmp = matrix[i][j];
                matrix[i][j] = matrix[n - 1 - i][j];
                matrix[n - 1 - i][j] = tmp;
            }
        }

        // 主对角线翻转
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < i; j++) {
                int tmp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = tmp;
            }
        }
    }
}

240 搜索二维矩阵2

从右上角开始搜索

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

链表

160 相交链表

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) {
            return null;
        }

        ListNode pA = headA, pB = headB;
        while (pA != pB) {
            pA = pA == null ? headB : pA.next;
            pB = pB == null ? headA : pB.next;
        }
        return pA;
    }
}

206 反转链表

三指针法

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        ListNode next = null;
        while (cur != null) {
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
}

234 回文链表

快慢指针

class Solution {
    public boolean isPalindrome(ListNode head) {
        if (head == null) return false;
        ListNode p = head, q = head;
        while (q.next != null && q.next.next != null) {
            p = p.next;
            q = q.next.next;
        }

        q = head;
        p = reverse(p.next);

        while (p != null) {
            if (p.val != q.val) {
                return false;
            }
            p = p.next;
            q = q.next;
        }

        return true;
    }

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

141 环形链表

public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null) {
            return false;
        }

        ListNode slow = head, fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) return true;
        }

        return false;
    }
}

142 找环形链表入口

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if (head == null) return null;
        ListNode slow = head, fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                slow = head;
                while (slow != fast) {
                    slow = slow.next;
                    fast = fast.next;
                }
                return slow;
            }
        }
        return null;
    }
}

21 合并两个有序链表

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if (list1 == null) return list2;
        if (list2 == null) return list1;
        if (list1.val < list2.val) {
            list1.next = mergeTwoLists(list1.next, list2);
            return list1;
        } else {
            list2.next = mergeTwoLists(list1, list2.next);
            return list2;
        }
    }
}

2 两数相加

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        if (l1 == null) return l2;
        if (l2 == null) return l1;
        int tmp = 0;
        ListNode head = null, tail = null;
        while (l1 != null || l2 != null) {
            int x = l1 == null ? 0 : l1.val;
            int y = l2 == null ? 0 : l2.val;
            int sum = x + y + tmp;
            if (head == null) {
                head = tail = new ListNode(sum % 10);
            } else {
                tail.next = new ListNode(sum % 10);
                tail = tail.next;
            }

            tmp = sum / 10;

            if (l1 != null) {
                l1 = l1.next;
            }

            if (l2 != null) {
                l2 = l2.next;
            }
        }

        if (tmp > 0) {
            tail.next = new ListNode(tmp);
        }

        return head;
    }
}

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

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode p = head, q = head;
        for (int i = 0; i < n - 1; i++) {
            q = q.next;
        }

        while (q.next != null) {
            p = p.next;
            q = q.next;
        }

        if (p == head) return head.next;

        ListNode pre = head;
        while (pre.next != p) {
            pre = pre.next;
        }

        pre.next = p.next;
        p.next = null;

        return head;
    }
}

24 两两交换链表中的节点

class Solution {
    public ListNode swapPairs(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }

        ListNode q = head.next;
        head.next = swapPairs(q.next);
        q.next = head;
        return q;
    }
}

25 K个一组翻转链表

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        if (head == null) return head;
        ListNode p = head;
        int len = 0;
        while (p != null && len < k) {
            p = p.next;
            len++;
        }

        if (len < k) return head;

        ListNode nextHead = p;


        ListNode pre = null;
        ListNode cur = head;
        ListNode next = null;
        while (cur != nextHead) {
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }

        head.next = reverseKGroup(nextHead, k);

        return pre;
    }
}

138 随机链表的复制

哈希存储节点

class Solution {
    public Node copyRandomList(Node head) {
        if (head == null) return head;

        Map<Node, Node> map = new HashMap<>();
        Node cur = head;
        while (cur != null) {
            map.put(cur, new Node(cur.val));
            cur = cur.next;
        }

        cur = head;
        while (cur != null) {
            map.get(cur).next = map.get(cur.next);
            map.get(cur).random = map.get(cur.random);
            cur = cur.next;
        }

        return map.get(head);
    }
}

148 排序链表

分治

class Solution {
    public ListNode sortList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }

        ListNode slow = head, fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        ListNode tmp = slow.next;
        slow.next = null;

        ListNode left = sortList(head);
        ListNode right = sortList(tmp);
        return mergeTwoList(left, right);
    }

    public ListNode mergeTwoList(ListNode l1, ListNode l2) {
        if (l1 == null) return l2;
        if (l2 == null) return l1;
        if (l1.val < l2.val) {
            l1.next = mergeTwoList(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoList(l1, l2.next);
            return l2;
        }
    }
}

23 合并K个升序链表

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        int n = lists.length;
        return merge(lists, 0, n - 1);
    }

    public ListNode merge(ListNode[] lists, int l, int r) {
        if (l == r) return lists[l];
        if (l > r) return null;

        int mid = l + r >> 1;
        ListNode left = merge(lists, l, mid);
        ListNode right = merge(lists, mid + 1, r);
        return mergeTwoList(left, right);
    }

    public ListNode mergeTwoList(ListNode l1, ListNode l2) {
        if (l1 == null) return l2;
        if (l2 == null) return l1;
        if (l1.val < l2.val) {
            l1.next = mergeTwoList(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoList(l1, l2.next);
            return l2;
        }
    }
}

146 LRU缓存

哈希+双向链表

class LRUCache {

    class DLinkedNode {
        int key;
        int val;
        DLinkedNode pre;
        DLinkedNode next;
        DLinkedNode(){}
        DLinkedNode(int key, int val) {
            this.key = key;
            this.val = val;
        }
    }

    private Map<Integer, DLinkedNode> cache = new HashMap<>();
    private int size;
    private int capacity;
    private DLinkedNode head, tail;

    public LRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.pre = head;
    }
    
    public int get(int key) {
        DLinkedNode node = cache.get(key);
        if (node == null) {
            return -1;
        }
        moveToHead(node);
        return node.val;
    }
    
    public void put(int key, int value) {
        DLinkedNode node = cache.get(key);
        if (node != null) {
            node.val = value;
            moveToHead(node);
        } else {
            DLinkedNode newNode = new DLinkedNode(key, value);
            cache.put(key, newNode);
            addToHead(newNode);
            size++;
            if (size > capacity) {
                removeTail();
                cache.remove(tail.key);
                size--;
            }
        }
    }

    private void addToHead(DLinkedNode node) {
        head.next.pre = node;
        node.next = head.next;
        node.pre = head;
        head.next = node;
    }

    private void moveToHead(DLinkedNode node) {
        node.next.pre = node.pre;
        node.pre.next = node.next;
        addToHead(node);
    }

    private void removeTail() {
        DLinkedNode node = tail.pre;
        cache.remove(node.key);
        node.pre.next = tail;
        tail.pre = node.pre;
    }
}

二叉树

94 二叉树的中序遍历

class Solution {
    List<Integer> res = new ArrayList<>();
    public List<Integer> inorderTraversal(TreeNode root) {
        inorder(root);
        return res;
    }

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

104 二叉树的最大深度

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

226 翻转二叉树

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

101 对称二叉树

class Solution {
    public boolean isSymmetric(TreeNode root) {
        return isSymmetric(root, root);
    }

    public boolean isSymmetric(TreeNode p, TreeNode q) {
        if (p == null && q == null) return true;

        if (p == null || q == null) return false;

        return (p.val == q.val) && isSymmetric(p.left, q.right) && 
            isSymmetric(p.right, q.left);
    }
}

543 二叉树的直径

class Solution {
    int res = 0;

    public int diameterOfBinaryTree(TreeNode root) {
        height(root);
        return res;
    }

    public int height(TreeNode root) {
        if (root == null) return 0;
        int left = height(root.left);
        int right = height(root.right);
        res = Math.max(res, left + right);
        return Math.max(left, right) + 1;
    }
}

102 二叉树的层序遍历

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if (root == null) return res;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (queue.size() != 0) {
            int len = queue.size();
            List<Integer> level = new ArrayList();
            for (int i = 0; i < len; i++) {
                TreeNode node = queue.poll();
                level.add(node.val);
                if (node.left != null) queue.add(node.left);
                if (node.right != null) queue.add(node.right);
            }
            res.add(level);
        }
        return res;
    }
}

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

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return BST(nums, 0, nums.length - 1);
    }

    private TreeNode BST(int[] nums, int l, int r) {
        if (l == r) return new TreeNode(nums[l]);
        if (l > r) return null;
        int mid = l + r >> 1;
        TreeNode root = new TreeNode(nums[mid]);
        root.left = BST(nums, l, mid - 1);
        root.right = BST(nums, mid + 1, r);
        return root;
    }
}

98 验证二叉搜索树

class Solution {
    public boolean isValidBST(TreeNode root) {
        if (root == null) return true;
        TreeNode leftMax = findMax(root.left);
        TreeNode rightMin = findMin(root.right);
        if (leftMax != null && leftMax.val >= root.val) return false;
        if (rightMin != null && rightMin.val <= root.val) return false;
        return isValidBST(root.left) && isValidBST(root.right);
    }

    private TreeNode findMax(TreeNode root) {
        if (root == null) return null;
        while (root.right != null) root = root.right;
        return root;
    }

    private TreeNode findMin(TreeNode root) {
        if (root == null) return null;
        while (root.left != null) root = root.left;
        return root;
    }
}

230 二叉搜索树中第K小的元素

class Solution {
    int count = 0;
    int res;
    public int kthSmallest(TreeNode root, int k) {
        order(root, k);
        return res;
    }

    private void order(TreeNode root, int k) {
        if (root == null) return;
        order(root.left, k);
        count++;
        if (count == k) res = root.val;
        order(root.right, k);
    }
}

199 二叉树的右视图

class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) return res;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (queue.size() != 0) {
            int len = queue.size();
            for (int i = 0; i < len; i++) {
                TreeNode node = queue.poll();
                if (i == len - 1) res.add(node.val);
                if (node.left != null) queue.add(node.left);
                if (node.right != null) queue.add(node.right);
            }
        }
        return res;
    }
}

114 二叉树展开为链表

找到左子树最右边的节点,指向右节点

class Solution {
    public void flatten(TreeNode root) {
        while (root != null) {
            if (root.left == null) {
                root = root.right;
            } else {
                TreeNode pre = findRight(root.left);
                pre.right = root.right;
                root.right = root.left;
                root.left = null;
                root = root.right;
            }
        }
    }

    public TreeNode findRight(TreeNode root) {
        if (root == null) return null;
        while (root.right != null) {
            root = root.right;
        }
        return root;
    }
}

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

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 buildTree(preorder, inorder, 0, n - 1, 0, n - 1);
    }

    private TreeNode buildTree(int[] preorder, int[] inorder, int pre_left, int pre_right, int in_left, int in_right) {
        if (pre_left > pre_right) return null;

        TreeNode root = new TreeNode(preorder[pre_left]);

        int loc = map.get(preorder[pre_left]);
        int left_size = loc - in_left;

        root.left = buildTree(preorder, inorder, pre_left + 1, pre_left + left_size, in_left, loc - 1);
        root.right = buildTree(preorder, inorder, pre_left + left_size + 1, pre_right, loc + 1, in_right);
        return root;
    }
}

437 路径总和 III

class Solution {
    // 以root为开头和以左右节点为开头
    public int pathSum(TreeNode root, int targetSum) {
        if (root == null) return 0;
        int res = dfs(root, targetSum);
        res += pathSum(root.left, targetSum);
        res += pathSum(root.right, targetSum);
        return res;
    }

    // 以root为开头的路径和
    private int dfs(TreeNode root, long targetSum) {
        int res = 0;
        if (root == null) return 0;
        if (targetSum == root.val) res++;
        res += dfs(root.left, targetSum - root.val);
        res += dfs(root.right, targetSum - root.val);
        return res;
    }
}

236 二叉树的最近公共祖先

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || root == p || root == q) return root;
        // 判断左右是否包含p或q
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if (left == null) return right;
        if (right == null) return left;
        return root;
    }
}

124 二叉树中的最大路径和

给每个节点都赋予一个权重

class Solution {
    int maxSum = Integer.MIN_VALUE;

    public int maxPathSum(TreeNode root) {
        maxGain(root);
        return maxSum;
    }

    public int maxGain(TreeNode root) {
        if (root == null) return Integer.MIN_VALUE;

        int left = Math.max(maxGain(root.left), 0);
        int right = Math.max(maxGain(root.right), 0);

        int price = root.val + left + right;

        maxSum = Math.max(maxSum, price);

        return Math.max(left, right) + root.val;
    }
}

图论

200 岛屿数量

class Solution {
    public int numIslands(char[][] grid) {
        int m = grid.length, n = grid[0].length;
        int res = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == '1') {
                    res++;
                    dfs(grid, i, j);
                }
            }
        }
        return res;
    }

    public void dfs(char[][] grid, int r, int c) {
        if (!isInArea(grid, r, c)) return;

        if (grid[r][c] != '1') return;

        if (grid[r][c] == '1') grid[r][c] = '2';

        dfs(grid, r + 1, c);
        dfs(grid, r - 1, c);
        dfs(grid, r, c + 1);
        dfs(grid, r, c - 1);
    }

    private boolean isInArea(char[][] grid, int r, int c) {
        return r >= 0 && r < grid.length &&
            c >= 0 && c < grid[0].length;
    }
}

994 腐烂的橘子

层序遍历

class Solution {
    public int orangesRotting(int[][] grid) {
        // 层序遍历
        int m = grid.length, n = grid[0].length;
        int count = 0; // 记录新鲜橘子的数量
        int res = 0;
        Queue<int[]> queue = new LinkedList<>();

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == 1) count++;
                else if (grid[i][j] == 2) {
                    queue.add(new int[]{i, j});
                }
            }
        }

        while (count > 0 && !queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                int[] pos = queue.poll();
                int r = pos[0], c = pos[1];
                if (r > 0 && grid[r - 1][c] == 1) {
                    grid[r - 1][c] = 2;
                    count--;
                    queue.add(new int[]{r - 1, c});
                }
                if (r < m - 1 && grid[r + 1][c] == 1) {
                    grid[r + 1][c] = 2;
                    count--;
                    queue.add(new int[]{r + 1, c});
                }
                if (c > 0 && grid[r][c - 1] == 1) {
                    grid[r][c - 1] = 2;
                    count--;
                    queue.add(new int[]{r, c - 1});
                }
                if (c < n - 1 && grid[r][c + 1] == 1) {
                    grid[r][c + 1] = 2;
                    count--;
                    queue.add(new int[]{r, c + 1});
                }
            }
            res++;
        }

        if (count > 0) {
            return -1;
        } else {
            return res;
        }
    }
}

207 课程表

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        int[] inDgree = new int[numCourses]; // 记录每门课程的入度
        // 记录每门课程的后置课程
        Map<Integer, List<Integer>> map = new HashMap<>();
        for (int i = 0; i < prerequisites.length; i++) {
            inDgree[prerequisites[i][0]]++;
            map.putIfAbsent(prerequisites[i][1], new ArrayList<>());
            map.get(prerequisites[i][1]).add(prerequisites[i][0]);
        }

        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < inDgree.length; i++) {
            if (inDgree[i] == 0) queue.add(i);
        }

        while (!queue.isEmpty()) {
            int course = queue.poll();
            numCourses--;
            for (int nextCourse : map.getOrDefault(course, new ArrayList<>())) {
                inDgree[nextCourse]--;
                if (inDgree[nextCourse] == 0) {
                    queue.add(nextCourse);
                }
            }
        }

        return numCourses == 0;
    }
}

208 前缀树

class Trie {
    private Trie[] children;
    private boolean isEnd;

    public Trie() {
        children = new Trie[26];
        isEnd = false;
    }
    
    public void insert(String word) {
        Trie node = this;
        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            int index = c - 'a';
            if (node.children[index] == null) {
                node.children[index] = new Trie();
            }
            node = node.children[index];
        }
        node.isEnd = true;
    }
    
    public boolean search(String word) {
        Trie node  = searchPrefix(word);
        return node != null && node.isEnd == true;
    }
    
    public boolean startsWith(String prefix) {
        return searchPrefix(prefix) != null;
    }

    private Trie searchPrefix(String prefix) {
        Trie node = this;
        for (int i = 0; i < prefix.length(); i++) {
            char c = prefix.charAt(i);
            int index = c - 'a';
            if (node.children[index] == null) {
                return null;
            }
            node = node.children[index];
        }
        return node;
    }
}

回溯

46 全排列

visited数组记录访问状态

class Solution {
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    int[] visited = new int[7];
    public List<List<Integer>> permute(int[] nums) {
        backtrack(nums);
        return res;
    }

    private void backtrack(int[] nums) {
        if (path.size() == nums.length) {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = 0; i < nums.length; i++) {
            if (visited[i] == 1) continue;
            path.add(nums[i]);
            visited[i] = 1;
            backtrack(nums);
            visited[i] = 0;
            path.remove(path.size() - 1);
        }
    }
}

78 子集

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

    public void backtrack(int[] nums, int i) {
        res.add(new ArrayList<>(tmp));
        for (int j = i; j < nums.length; j++) {
            tmp.add(nums[j]);
            backtrack(nums, j + 1);
            tmp.remove(tmp.size() - 1);
        }
    }
}

17 电话号码的字母组合

class Solution {
    List<String> res = new ArrayList<>();
    StringBuilder path = new StringBuilder();
    // 双括号法初始化map
    HashMap<Character, String> phoneMap = new HashMap<>(){{
        put('2', "abc");
        put('3', "def");
        put('4', "ghi");
        put('5', "jkl");
        put('6', "mno");
        put('7', "pqrs");
        put('8', "tuv");
        put('9', "wxyz");
    }};

    public List<String> letterCombinations(String digits) {
        if (digits.length() == 0) return res;
        backtrack(digits, 0);
        return res;
    }

    private void backtrack(String digits, int index) {
        if (index == digits.length()) {
            res.add(path.toString());
            return;
        }

        String str = phoneMap.get(digits.charAt(index));
        for (int i = 0; i < str.length(); i++) {
            path.append(str.charAt(i));
            backtrack(digits, index + 1);
            path.deleteCharAt(path.length() - 1);
        }
    }
}

39 组合总和

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

    private void backtrack(int[] candidates, int target, int start) {
        if (target < 0) return;
        if (target == 0) {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = start; i < candidates.length; i++) {
            path.add(candidates[i]);
            backtrack(candidates, target - candidates[i], i);
            path.remove(path.size() - 1);
        }
    }
}

22 括号生成

class Solution {
    List<String> res = new ArrayList<>();
    public List<String> generateParenthesis(int n) {
        backtrack(new StringBuilder(), 0, 0, n);
        return res;
    }

    public void backtrack(StringBuilder builder, int left, int right, int n) {
        if (left + right == n * 2) {
            res.add(builder.toString());
            return;
        }

        if (left < n) {
            builder.append('(');
            backtrack(builder, left + 1, right, n);
            builder.deleteCharAt(builder.length() - 1);
        }

        if (right < left) {
            builder.append(')');
            backtrack(builder, left, right + 1, n);
            builder.deleteCharAt(builder.length() - 1);
        }
    }
}

79 单词搜索

class Solution {
    public boolean exist(char[][] board, String word) {
        char[] words = word.toCharArray();
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                if (dfs(board, words, i, j, 0)) return true;
            }
        }
        return false;
    }

    public boolean dfs(char[][] board, char[] words, int i, int j, int k) {
        if (i < 0 || i >= board.length || j < 0 || j >= board[0].length 
            || board[i][j] != words[k]) return false;
        if (k == words.length - 1) return true;
        board[i][j] = '\0';
        boolean res = dfs(board, words, i + 1, j, k + 1) ||
                    dfs(board, words, i - 1, j, k + 1) ||
                    dfs(board, words, i, j + 1, k + 1) ||
                    dfs(board, words, i, j - 1, k + 1);
        board[i][j] = words[k];
        return res;
    }
}

131 分割回文串

class Solution {
    List<List<String>> res = new ArrayList<>();
    List<String> path = new ArrayList<>();
    public List<List<String>> partition(String s) {
        backtrack(s, 0);
        return res;
    }

    public void backtrack(String s, int start) {
        if (start >= s.length()) {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = start; i < s.length(); i++) {
            if (isPalindrome(s, start, i)) {
                path.add(s.substring(start, i + 1));
            } else {
                continue;
            }
            backtrack(s, i + 1);
            path.remove(path.size() - 1);
        }
    }

    public boolean isPalindrome(String s, int l, int r) {
        while (l < r) {
            if (s.charAt(l) != s.charAt(r)) return false;
            l++;
            r--;
        }
        return true;
    }
    
}

51 N皇后

class Solution {
    List<List<String>> res = new ArrayList<>();
    List<String> path = new ArrayList<>();
    int[] chessboard; // 存储第i行的皇后在第几列
    public List<List<String>> solveNQueens(int n) {
        chessboard = new int[n];
        backtrack(n, 0);
        return res;
    }

    public void backtrack(int n, int r) {
        if (path.size() == n) {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int c = 0; c < n; c++) {
            if (!isValid(r, c)) continue;
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j < n; j++) {
                if (j != c) sb.append('.');
                else sb.append('Q');
            }

            path.add(sb.toString());
            chessboard[r] = c;
            backtrack(n, r + 1);
            path.remove(path.size() - 1);
            chessboard[r] = 0;
        }
    }

    private boolean isValid(int row, int col) {
        for (int i = 0; i < row; i++) {
            if (chessboard[i] == col) return false;
            if (chessboard[i] + i == row + col) return false;
            if (chessboard[i] - i == col - row) return false;
        }
        return true;
    }
}

二分

35 搜索插入位置

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

74 搜索二维矩阵

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

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

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

        left = 0;
        right = n - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target) {
                last = mid;
                left = mid + 1;
            }
            else if (nums[mid] <= target) left = mid + 1;
            else right = mid - 1;
        }
        if (first == n) return new int[]{-1, -1};
        else return new int[]{first, last};
    }
}

153 寻找旋转排序数组的最小值

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

33 搜索排序旋转数组

找分界点+左右分别查找

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

        if (split == 0) {
            return search(nums, target, 0, n - 1);
        }

        if (nums[0] > target) {
            return search(nums, target, split, n - 1);
        }
        return search(nums, target, 0, split - 1);
    }

    public int search(int[] nums, int target, int l, int r) {
        while (l <= r) {
            int mid = (l + r) / 2;
            if (nums[mid] == target) return mid;
            else if (nums[mid] > target) r = mid - 1;
            else l = mid + 1;
        }
        return -1;
    }
}

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

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m = nums1.length;
        int n = nums2.length;
        int left = (m + n + 1) / 2;
        int right = (m + n + 2) / 2;
        //将偶数和奇数的情况合并,如果是奇数,会求两次同样的 k
        return (getKth(nums1, 0, m - 1, nums2, 0, n - 1, left) + getKth(nums1, 0, m - 1, nums2, 0, n - 1, right)) * 0.5;
    }

    private int getKth(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k) {
        int len1 = end1 - start1 + 1;
        int len2 = end2 - start2 + 1;
        // 让len1长度小于len2,这样能保证如果有数组为空,一定是len1
        if (len1 > len2) return getKth(nums2, start2, end2, nums1, start1, end1, k);
        if (len1 == 0) return nums2[start2 + k - 1];

        if (k == 1) return Math.min(nums1[start1], nums2[start2]);
        
        int i = start1 + Math.min(len1, k / 2) - 1;
        int j = start2 + Math.min(len2, k / 2) - 1;

        if (nums1[i] > nums2[j]) {
            return getKth(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1));
        } else {
            return getKth(nums1, i + 1, end1, nums2, start2, end2, k - (i - start1 + 1));
        }
    }
}

20 有效的括号

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        for (Character c : s.toCharArray()) {
            if (c == '(') stack.push(')');
            else if (c == '[') stack.push(']');
            else if (c == '{') stack.push('}');
            else if (stack.empty() || c != stack.pop()) return false;
        }
        if (stack.empty()) return true;
        return false;
    }
}

155 最小栈

class MinStack {
    Deque<Integer> xStack;
    Deque<Integer> minStack;

    public MinStack() {
        xStack = new LinkedList<Integer>();
        minStack = new LinkedList<Integer>();
        minStack.push(Integer.MAX_VALUE);
    }
    
    public void push(int val) {
        xStack.push(val);
        minStack.push(Math.min(minStack.peek(), val));
    }
    
    public void pop() {
        xStack.pop();
        minStack.pop();
    }
    
    public int top() {
        return xStack.peek();
    }
    
    public int getMin() {
        return minStack.peek();
    }
}

394 字符串解码

class Solution {
    public String decodeString(String s) {
        Deque<Integer> countStack = new ArrayDeque<>();
        Deque<String> stringStack = new ArrayDeque<>();
        String curString = "";
        int k = 0;

        for (char ch : s.toCharArray()) {
            if (Character.isDigit(ch)) {
                k = k * 10 + (ch - '0'); // 处理多位数
            } else if (ch == '[') {
                countStack.push(k);
                stringStack.push(curString);
                curString = "";
                k = 0;
            } else if (ch == ']') {
                // 遇到 ']',解码
                StringBuilder temp = new StringBuilder(stringStack.pop());
                int repeatTimes = countStack.pop();
                for (int i = 0; i < repeatTimes; i++) {
                    temp.append(curString); // 重复当前字符串
                }
                curString = temp.toString(); // 更新当前字符串
            } else {
                // 如果是字母,直接加到当前字符串
                curString += ch;
            }
        }
        return curString;
    }
}

739 每日温度

单调栈

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int n = temperatures.length;
        Deque<Integer> st = new LinkedList<>();
        int[] res = new int[n];
        for (int i = 0; i < n; i++) {
            int cur = temperatures[i];
            while (!st.isEmpty() && cur > temperatures[st.peek()]) {
                int preIndex = st.pop();
                res[preIndex] = i - preIndex;
            }
            st.push(i);
        }
        return res;
    }
}

84 柱状图中的最大矩形

单调栈

class Solution {
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        int[] left = new int[n];
        Deque<Integer> st = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            int x = heights[i];
            while (!st.isEmpty() && x <= heights[st.peek()]) {
                st.pop();
            }
            left[i] = st.isEmpty() ? -1 : st.peek();
            st.push(i);
        }

        int[] right = new int[n];
        st.clear();
        for (int i = n - 1; i >= 0; i--) {
            int x = heights[i];
            while (!st.isEmpty() && x <= heights[st.peek()]) {
                st.pop();
            }
            right[i] = st.isEmpty() ? n : st.peek();
            st.push(i);
        }

        int res = 0;
        for (int i = 0; i < n; i++) {
            res = Math.max(res, heights[i] * (right[i] - left[i] - 1));
        }

        return res;
    }
}

215 数组中第K个最大元素

快速选择

class Solution {
    public int findKthLargest(int[] nums, int k) {
        int n = nums.length;
        return quickSelect(nums, n - k, 0, n - 1);
    }

    public int quickSelect(int[] nums, int k, int l, int r) {
        if (l == r) return nums[l];
        int i = l - 1, j = r + 1, x = nums[l + r >> 1];
        while (i < j) {
            do i++; while (nums[i] < x);
            do j--; while (nums[j] > x);
            if (i < j) {
                int tmp = nums[i];
                nums[i] = nums[j];
                nums[j] = tmp;
            }
        }

        if (k <= j) return quickSelect(nums, k, l, j);
        else return quickSelect(nums, k, j + 1, r);
    }
}

347 前K个高频元素

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        // 使用字典,统计每个元素出现的次数,元素为键,元素出现的次数为值
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
        }

        List<Integer> res = new ArrayList<>();

        // 将频率作为数组下标,对于出现频率不同的数字集合,存入对应的数组下标
        List<Integer>[] list = new List[nums.length + 1];
        for (int key : map.keySet()) {
            int i = map.get(key);
            if (list[i] == null) {
                list[i] = new ArrayList<>();
            }
            list[i].add(key);
        }

        // 倒序遍历数组获取出现顺序从大到小的排列
        for (int i = list.length - 1; i >= 0 && res.size() < k; i--) {
            if (list[i] == null) continue;
            res.addAll(list[i]);
        }

        int[] ans = new int[k];
        for (int i = 0; i < k; i++) {
            ans[i] = res.get(i);
        }
        return ans;
    }
}

295 数据流的中位数

class MedianFinder {

    Queue<Integer> small, big;

    public MedianFinder() {
        small = new PriorityQueue<>((x, y) -> (y - x));
        big = new PriorityQueue<>(); // 默认是小顶堆,存储较大的一半
        // big里的数可能比small里多一个
    }
    
    public void addNum(int num) {
        if (small.size() == big.size()) {
            small.add(num);
            big.add(small.poll());
        } else {
            big.add(num);
            small.add(big.poll());
        }
    }
    
    public double findMedian() {
        return small.size() == big.size() ? (small.peek() + big.peek()) / 2.0 : big.peek();
    }
}

贪心算法

121 买卖股票的最佳时机

class Solution {
    public int maxProfit(int[] prices) {
        int minPrice = prices[0];
        int res = 0;
        for (int price : prices) {
            if (price > minPrice) {
                res = Math.max(res, price - minPrice);
            } else {
                minPrice = price;
            }
        }
        return res;
    }
}

55 跳跃游戏

class Solution {
    public boolean canJump(int[] nums) {
        int right = 0;
        for (int i = 0; i < nums.length; i++) {
            if (i > right) break;
            right = Math.max(right, i + nums[i]);
        }
        return right >= nums.length - 1;
    }
}

45 跳跃游戏2

class Solution {
    public int jump(int[] nums) {
        int res = 0;
        int end = 0;
        int right = 0;
        // 这里要注意i < nums.length - 1,最后一步肯定跳到终点了
        for (int i = 0; i < nums.length - 1; i++) {
            right = Math.max(right, i + nums[i]);
            if (i == end) {
                end = right;
                res++;
            }
        }
        return res;
    }
}

763 划分字母区间

class Solution {
    public List<Integer> partitionLabels(String s) {
        int[] last = new int[26];
        for (int i = 0; i < s.length(); i++) {
            last[s.charAt(i) - 'a'] = i;
        }

        List<Integer> res = new ArrayList<>();
        int start = 0, end = 0;
        for (int i = 0; i < s.length(); i++) {
            end = Math.max(end, last[s.charAt(i) - 'a']);
            if (i == end) {
                res.add(end - start + 1);
                start = end + 1;
            }
        }
        return res;
    }
}

动态规划

70 爬楼梯

class Solution {
    public int climbStairs(int n) {
        if (n == 1) return 1;
        if (n == 2) return 2;
        int c1 = 1;
        int c2 = 2;
        int res = 0;
        for (int i = 3; i <= n; i++) {
            res = c1 + c2;
            c1 = c2;
            c2 = res;
        }
        return res;
    }
}

118 杨辉三角

class Solution {
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> res = new ArrayList<>();
        res.add(Arrays.asList(1));
        for (int row = 1; row < numRows; row++) {
            List<Integer> level = new ArrayList<>();
            level.add(1);
            for (int i = 1; i < row; i++) {
                level.add(res.get(row - 1).get(i - 1) + res.get(row - 1).get(i));
            }
            level.add(1);
            res.add(level);
        }
        return res;
    }
}

198 打家劫舍

class Solution {
    public int rob(int[] nums) {
        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 - 1], dp[i - 2] + nums[i]);
        }
        return dp[n - 1];
    }
}

279 完全平方数

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

322 零钱兑换

class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, 2 * amount);
        dp[0] = 0;
        for (int i = 1; i <= amount; i++) {
            for (int j = 0; j < coins.length; j++) {
                if (coins[j] <= i) {
                    dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
                }
            }
        }
        return dp[amount] > amount ? -1 : dp[amount];
    }
}

139 单词拆分

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        Set<String> set = 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] && set.contains(s.substring(j, i))) {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.length()];
    }
}

300 最长递增子序列

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

152 乘积最大子数组

class Solution {
    public int maxProduct(int[] nums) {
        int res = Integer.MIN_VALUE;
        int imax = 1, imin = 1;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] < 0) {
                int tmp = imax;
                imax = imin;
                imin = tmp;
            }
            imax = Math.max(imax * nums[i], nums[i]);
            imin = Math.min(imin * nums[i], nums[i]);

            res = Math.max(res, imax);
        }
        return res;
    }
}

416 分割等和子集

01背包问题

class Solution {
    public boolean canPartition(int[] nums) {
        int n = nums.length;
        int total = 0;
        for (int num : nums) total += num;
        if (total % 2 == 1) return false;
        int target = total / 2;


        int[]dp = new int[target + 1];

        for (int i = 1; i <= n; i++) {
            for (int j = target; j >= nums[i - 1]; j--) {
                dp[j] = Math.max(dp[j], dp[j - nums[i - 1]] + nums[i - 1]);
            }
        }

        int[][] f = new int[n + 1][target + 1];

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= target; j++) {
                if (nums[i - 1] > j) {
                    f[i][j] = f[i - 1][j];
                } else {
                    f[i][j] = Math.max(f[i - 1][j], f[i - 1][j - nums[i - 1]] + nums[i - 1]);
                }
            }
        }
 
        return f[n][target] == target;

    }
}

32 最长有效括号

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

多维动态规划

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];
    }
}

64 最小路径和

class Solution {
    public int minPathSum(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        int[][] dp = new int[m][n];
        dp[0][0] = grid[0][0];
        for (int i = 1; i < m; i++) {
            dp[i][0] = dp[i - 1][0] + grid[i][0];
        }
        for (int i = 1; i < n; i++) {
            dp[0][i] = dp[0][i - 1] + grid[0][i];
        }

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

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

5 最长回文子串

class Solution {
    public String longestPalindrome(String s) {
        if (s == null || s.length() < 2) return s;
        int strLen = s.length();
        int maxStart = 0;
        int maxEnd = 0;
        int maxLen = 1;

        boolean[][] dp = new boolean[strLen][strLen];
        for (int r = 1; r < strLen; r++) {
            for (int l = 0; l < r; l++) {
                if (s.charAt(l) == s.charAt(r) && 
                    (r - l <= 2 || dp[l + 1][r - 1])) { // r - 1 <= 2是精髓
                        dp[l][r] = true;
                        if (r - l + 1 > maxLen) {
                            maxLen = r - l + 1;
                            maxStart = l;
                            maxEnd = r;
                        }
                    }
            }
        }

        return s.substring(maxStart, maxEnd + 1);
    }
}

1143 最长公共子序列

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int m = text1.length();
        int n = text2.length();
        int[][] dp = new int[m + 1][n + 1];
        for (int i = 1; i <= m; i++) {
            char c1 = text1.charAt(i - 1);
            for (int j = 1; j <= n; j++) {
                char c2 = text2.charAt(j - 1);
                if (c1 == c2) {
                    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[m][n];
    }
}

72 编辑距离

思路:用 D[i][j] 表示 A 的前 i 个字母和 B 的前 j 个字母之间的编辑距离。

class Solution {
    public int minDistance(String word1, String word2) {
        int m = word1.length();
        int n = word2.length();
        if (m == 0 || n == 0) {
            return m + n;
        }
        int[][] dp = new int[m + 1][n + 1];

        for (int i = 0; i < m + 1; i++) {
            dp[i][0] = i;
        }
        for (int j = 0; j < n + 1; j++) {
            dp[0][j] = j;
        }

        for (int i = 1; i < m + 1; i++) {
            for (int j = 1; j < n + 1; j++) {
                int left = dp[i - 1][j] + 1;
                int up = dp[i][j - 1] + 1;
                int up_left = dp[i - 1][j - 1];
                if (word1.charAt(i - 1) != word2.charAt(j - 1)) {
                    up_left += 1;
                }
                dp[i][j] = Math.min(left, Math.min(up, up_left));
            }
        }
        return dp[m][n];
    }
}

技巧

136 只出现一次的数字

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

169 多数元素

摩尔计数

class Solution {
    public int majorityElement(int[] nums) {
        int res = nums[0];
        int count = 0;
        for (int x : nums) {
            if (count == 0) {
                res = x;
                count++;
                continue;
            }
            if (x == res) count++;
            else count--;
        }
        return res;
    }
}

75 颜色分类

两次遍历

class Solution {
    public void sortColors(int[] nums) {
        int n = nums.length;
        int p0 = 0, p2 = n - 1;
        for (int i = 0; i <= p2; i++) {
            while (i <= p2 && nums[i] == 2) {
                int tmp = nums[i];
                nums[i] = nums[p2];
                nums[p2] = tmp;
                p2--;
            }
            if (nums[i] == 0) {
                int tmp = nums[i];
                nums[i] = nums[p0];
                nums[p0] = tmp;
                p0++;
            }
        }
    }
}

一次遍历:1正常处理,0要特殊处理

class Solution {
    public void sortColors(int[] nums) {
        int n = nums.length;
        int p0 = 0, p1 = 0;
        for (int i = 0; i < n; i++) {
            if (nums[i] == 1) {
                swap(nums, i, p1);
                p1++;
            } else if (nums[i] == 0) {
                swap(nums, i, p0);
                if (p0 < p1) {
                    swap(nums, i, p1);
                }
                p0++;
                p1++;
            }
        }
    }

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

31 下一个排列

class Solution {
    public void nextPermutation(int[] nums) {
        // 134652
        int n = nums.length;
        if (n <= 1) return;

        // 1.倒序找到46的位置,确定较小的数4
        int i = n - 2;
        while (i >= 0 && nums[i] >= nums[i + 1]) i--;

        // 若i<0则已经到达了最后一个排列
        if (i >= 0) {
            // 2.倒序找到第一个比4大的数
            int j = n - 1;
            while (j >= 0 && nums[i] >= nums[j]) j--;
            int tmp = nums[i];
            nums[i] = nums[j];
            nums[j] = tmp;
        }

        // 135642
        // 3.翻转4后面的数,使其递增排列 135246
        int left = i + 1;
        int right = n - 1;
        while (left < right) {
            int tmp = nums[left];
            nums[left] = nums[right];
            nums[right] = tmp;
            left++;
            right--;
        }
    }
}

287 寻找重复数

快慢指针,找环入口

class Solution {
    public int findDuplicate(int[] nums) {
        int slow = 0, fast = 0;
        do {
            slow = nums[slow];
            fast = nums[nums[fast]];
        } while (slow != fast);

        slow = 0;
        while (slow != fast) {
            slow = nums[slow];
            fast = nums[fast];
        }
        
        return slow;
    }
}

你可能感兴趣的:(java,力扣)