按面试出现频率从高到低排序,每道题包含题目描述、解题思路分析和Java实现。适合架构师面试前快速复习算法基本功。
难度标记
1. 🟡 两数之和(Two Sum)- LeetCode 1
题目:给定一个整数数组和一个目标值,找出数组中和为目标值的两个数的下标。
分析:暴力解法O(n²),用HashMap存储已遍历的值和下标,一次遍历O(n)解决。面试最高频题,考察哈希表的基本应用。
1 2 3 4 5 6 7 8 9 10 11 public int [] twoSum(int [] nums, int target) { Map<Integer, Integer> map = new HashMap <>(); for (int i = 0 ; i < nums.length; i++) { int complement = target - nums[i]; if (map.containsKey(complement)) { return new int []{map.get(complement), i}; } map.put(nums[i], i); } return new int [0 ]; }
时间复杂度:O(n),空间复杂度:O(n)
2. 🟡 两数相加(Add Two Numbers)- LeetCode 2
题目:两个非空链表表示两个非负整数(逆序存储),返回它们的和,同样用链表表示。
分析:模拟竖式加法,逐位相加并处理进位。注意最后可能还有进位需要新增节点。链表操作基本功。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public ListNode addTwoNumbers (ListNode l1, ListNode l2) { ListNode dummy = new ListNode (0 ); ListNode curr = dummy; int carry = 0 ; while (l1 != null || l2 != null || carry != 0 ) { int sum = carry; if (l1 != null ) { sum += l1.val; l1 = l1.next; } if (l2 != null ) { sum += l2.val; l2 = l2.next; } carry = sum / 10 ; curr.next = new ListNode (sum % 10 ); curr = curr.next; } return dummy.next; }
时间复杂度:O(max(m,n)),空间复杂度:O(max(m,n))
3. 🟡 无重复字符的最长子串(Longest Substring Without Repeating Characters)- LeetCode 3
题目:给定一个字符串,找出不含重复字符的最长子串的长度。
分析:经典滑动窗口题。用HashSet或数组记录窗口内的字符,右指针扩展窗口,遇到重复字符时左指针收缩。
1 2 3 4 5 6 7 8 9 10 public int lengthOfLongestSubstring (String s) { int [] index = new int [128 ]; int maxLen = 0 , left = 0 ; for (int right = 0 ; right < s.length(); right++) { left = Math.max(left, index[s.charAt(right)]); maxLen = Math.max(maxLen, right - left + 1 ); index[s.charAt(right)] = right + 1 ; } return maxLen; }
时间复杂度:O(n),空间复杂度:O(1)(固定128大小数组)
4. 🔴 寻找两个正序数组的中位数(Median of Two Sorted Arrays)- LeetCode 4
题目:给定两个正序数组,找出这两个数组的中位数,要求时间复杂度O(log(m+n))。
分析:二分查找。在较短的数组上二分,找到一个分割点使得左半部分的最大值≤右半部分的最小值。面试Hard题经典代表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public double findMedianSortedArrays (int [] nums1, int [] nums2) { if (nums1.length > nums2.length) return findMedianSortedArrays(nums2, nums1); int m = nums1.length, n = nums2.length; int lo = 0 , hi = m; while (lo <= hi) { int i = (lo + hi) / 2 ; int j = (m + n + 1 ) / 2 - i; int maxLeft1 = (i == 0 ) ? Integer.MIN_VALUE : nums1[i - 1 ]; int minRight1 = (i == m) ? Integer.MAX_VALUE : nums1[i]; int maxLeft2 = (j == 0 ) ? Integer.MIN_VALUE : nums2[j - 1 ]; int minRight2 = (j == n) ? Integer.MAX_VALUE : nums2[j]; if (maxLeft1 <= minRight2 && maxLeft2 <= minRight1) { if ((m + n) % 2 == 0 ) return (Math.max(maxLeft1, maxLeft2) + Math.min(minRight1, minRight2)) / 2.0 ; else return Math.max(maxLeft1, maxLeft2); } else if (maxLeft1 > minRight2) { hi = i - 1 ; } else { lo = i + 1 ; } } return 0 ; }
时间复杂度:O(log(min(m,n))),空间复杂度:O(1)
5. 🟡 最长回文子串(Longest Palindromic Substring)- LeetCode 5
题目:给定一个字符串,找到最长的回文子串。
分析:中心扩展法最直观。每个字符(和每两个相邻字符之间)作为中心向两边扩展。也可以用动态规划或Manacher算法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public String longestPalindrome (String s) { int start = 0 , maxLen = 0 ; for (int i = 0 ; i < s.length(); i++) { int len1 = expand(s, i, i); int len2 = expand(s, i, i + 1 ); int len = Math.max(len1, len2); if (len > maxLen) { maxLen = len; start = i - (len - 1 ) / 2 ; } } return s.substring(start, start + maxLen); } private int expand (String s, int left, int right) { while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) { left--; right++; } return right - left - 1 ; }
时间复杂度:O(n²),空间复杂度:O(1)
6. 🟡 盛最多水的容器(Container With Most Water)- LeetCode 11
题目:给定n个非负整数表示n条垂线,找出两条线与x轴构成的容器能容纳最多的水。
分析:双指针从两端向中间收缩。每次移动较短的那条线,因为移动较长的线不可能得到更大的面积。贪心思想。
1 2 3 4 5 6 7 8 9 10 public int maxArea (int [] height) { int left = 0 , right = height.length - 1 , max = 0 ; while (left < right) { int area = Math.min(height[left], height[right]) * (right - left); max = Math.max(max, area); if (height[left] < height[right]) left++; else right--; } return max; }
时间复杂度:O(n),空间复杂度:O(1)
7. 🟡 三数之和(3Sum)- LeetCode 15
题目:找出数组中所有和为0的三元组,不能包含重复的三元组。
分析:排序+双指针。固定一个数,对剩余部分用双指针找两数之和。关键是去重逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public List<List<Integer>> threeSum (int [] nums) { List<List<Integer>> res = new ArrayList <>(); Arrays.sort(nums); for (int i = 0 ; i < nums.length - 2 ; i++) { if (nums[i] > 0 ) break ; if (i > 0 && nums[i] == nums[i - 1 ]) continue ; int left = i + 1 , right = nums.length - 1 ; while (left < right) { int sum = nums[i] + nums[left] + nums[right]; if (sum == 0 ) { res.add(Arrays.asList(nums[i], nums[left], nums[right])); while (left < right && nums[left] == nums[left + 1 ]) left++; while (left < right && nums[right] == nums[right - 1 ]) right--; left++; right--; } else if (sum < 0 ) left++; else right--; } } return res; }
时间复杂度:O(n²),空间复杂度:O(1)(不计输出)
8. 🟡 电话号码的字母组合(Letter Combinations of a Phone Number)- LeetCode 17
题目:给定一个仅包含数字2-9的字符串,返回所有它能表示的字母组合。
分析:经典回溯题。每个数字对应几个字母,逐位选择,递归生成所有组合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public List<String> letterCombinations (String digits) { List<String> res = new ArrayList <>(); if (digits.isEmpty()) return res; String[] map = {"" , "" , "abc" , "def" , "ghi" , "jkl" , "mno" , "pqrs" , "tuv" , "wxyz" }; backtrack(res, new StringBuilder (), digits, map, 0 ); return res; } private void backtrack (List<String> res, StringBuilder sb, String digits, String[] map, int idx) { if (idx == digits.length()) { res.add(sb.toString()); return ; } for (char c : map[digits.charAt(idx) - '0' ].toCharArray()) { sb.append(c); backtrack(res, sb, digits, map, idx + 1 ); sb.deleteCharAt(sb.length() - 1 ); } }
时间复杂度:O(4^n),空间复杂度:O(n)
9. 🟡 删除链表的倒数第N个节点(Remove Nth Node From End of List)- LeetCode 19
题目:删除链表的倒数第n个节点,返回链表头节点。要求一趟扫描。
分析:快慢指针。快指针先走n步,然后快慢指针同时走,快指针到末尾时慢指针指向待删除节点的前一个。
1 2 3 4 5 6 7 8 public ListNode removeNthFromEnd (ListNode head, int n) { ListNode dummy = new ListNode (0 , head); ListNode fast = dummy, slow = dummy; for (int i = 0 ; i <= n; i++) fast = fast.next; while (fast != null ) { fast = fast.next; slow = slow.next; } slow.next = slow.next.next; return dummy.next; }
时间复杂度:O(L),空间复杂度:O(1)
10. 🟢 有效的括号(Valid Parentheses)- LeetCode 20
题目:给定一个只包含括号的字符串,判断是否有效(正确闭合)。
分析:栈的经典应用。遇到左括号入栈,遇到右括号检查栈顶是否匹配。
1 2 3 4 5 6 7 8 9 10 public boolean isValid (String s) { Deque<Character> stack = new ArrayDeque <>(); for (char c : s.toCharArray()) { if (c == '(' ) stack.push(')' ); else if (c == '[' ) stack.push(']' ); else if (c == '{' ) stack.push('}' ); else if (stack.isEmpty() || stack.pop() != c) return false ; } return stack.isEmpty(); }
时间复杂度:O(n),空间复杂度:O(n)
11. 🟢 合并两个有序链表(Merge Two Sorted Lists)- LeetCode 21
题目:将两个升序链表合并为一个新的升序链表。
分析:双指针逐一比较,也可以递归实现。基础链表操作。
1 2 3 4 5 6 7 8 9 10 public ListNode mergeTwoLists (ListNode l1, ListNode l2) { ListNode dummy = new ListNode (0 ), curr = dummy; while (l1 != null && l2 != null ) { if (l1.val <= l2.val) { curr.next = l1; l1 = l1.next; } else { curr.next = l2; l2 = l2.next; } curr = curr.next; } curr.next = (l1 != null ) ? l1 : l2; return dummy.next; }
时间复杂度:O(m+n),空间复杂度:O(1)
12. 🟡 括号生成(Generate Parentheses)- LeetCode 22
题目:生成所有由n对括号组成的有效组合。
分析:回溯法。维护左括号和右括号的剩余数量,左括号可以随时放,右括号只有在剩余数量大于左括号时才能放。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public List<String> generateParenthesis (int n) { List<String> res = new ArrayList <>(); backtrack(res, new StringBuilder (), 0 , 0 , n); return res; } private void backtrack (List<String> res, StringBuilder sb, int open, int close, int n) { if (sb.length() == 2 * n) { res.add(sb.toString()); return ; } if (open < n) { sb.append('(' ); backtrack(res, sb, open + 1 , close, n); sb.deleteCharAt(sb.length() - 1 ); } if (close < open) { sb.append(')' ); backtrack(res, sb, open, close + 1 , n); sb.deleteCharAt(sb.length() - 1 ); } }
时间复杂度:O(4^n/√n)(卡特兰数),空间复杂度:O(n)
13. 🔴 合并K个升序链表(Merge k Sorted Lists)- LeetCode 23
题目:合并k个升序链表,返回合并后的升序链表。
分析:优先队列(最小堆)。将每个链表的头节点入堆,每次取最小的节点,再将其next入堆。也可以分治合并。
1 2 3 4 5 6 7 8 9 10 11 12 public ListNode mergeKLists (ListNode[] lists) { PriorityQueue<ListNode> pq = new PriorityQueue <>((a, b) -> a.val - b.val); for (ListNode node : lists) if (node != null ) pq.offer(node); ListNode dummy = new ListNode (0 ), curr = dummy; while (!pq.isEmpty()) { ListNode node = pq.poll(); curr.next = node; curr = curr.next; if (node.next != null ) pq.offer(node.next); } return dummy.next; }
时间复杂度:O(N·log k),空间复杂度:O(k)
14. 🟡 下一个排列(Next Permutation)- LeetCode 31
题目:找出数组的下一个字典序排列。如果已经是最大排列,则重排为最小排列。
分析:从右往左找第一个下降的位置i,再从右往左找第一个大于nums[i]的位置j,交换后反转i+1到末尾。
1 2 3 4 5 6 7 8 9 10 11 12 13 public void nextPermutation (int [] nums) { int i = nums.length - 2 ; while (i >= 0 && nums[i] >= nums[i + 1 ]) i--; if (i >= 0 ) { int j = nums.length - 1 ; while (nums[j] <= nums[i]) j--; swap(nums, i, j); } reverse(nums, i + 1 , nums.length - 1 ); } private void swap (int [] nums, int i, int j) { int t = nums[i]; nums[i] = nums[j]; nums[j] = t; }private void reverse (int [] nums, int l, int r) { while (l < r) swap(nums, l++, r--); }
时间复杂度:O(n),空间复杂度:O(1)
15. 🔴 最长有效括号(Longest Valid Parentheses)- LeetCode 32
题目:给定一个只包含’(‘和’)’的字符串,找出最长有效括号子串的长度。
分析:用栈存储下标。栈底保持最后一个未匹配的右括号下标作为分隔符。也可以用DP或双向扫描。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public int longestValidParentheses (String s) { Deque<Integer> stack = new ArrayDeque <>(); stack.push(-1 ); int max = 0 ; for (int i = 0 ; i < s.length(); i++) { if (s.charAt(i) == '(' ) { stack.push(i); } else { stack.pop(); if (stack.isEmpty()) stack.push(i); else max = Math.max(max, i - stack.peek()); } } return max; }
时间复杂度:O(n),空间复杂度:O(n)
16. 🟡 搜索旋转排序数组(Search in Rotated Sorted Array)- LeetCode 33
题目:在旋转排序数组中搜索目标值,要求O(log n)。
分析:二分查找变体。每次判断哪半边是有序的,再判断target是否在有序的那半边。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public int search (int [] nums, int target) { int lo = 0 , hi = nums.length - 1 ; while (lo <= hi) { int mid = lo + (hi - lo) / 2 ; if (nums[mid] == target) return mid; if (nums[lo] <= nums[mid]) { if (target >= nums[lo] && target < nums[mid]) hi = mid - 1 ; else lo = mid + 1 ; } else { if (target > nums[mid] && target <= nums[hi]) lo = mid + 1 ; else hi = mid - 1 ; } } return -1 ; }
时间复杂度:O(log n),空间复杂度:O(1)
17. 🟡 在排序数组中查找元素的第一个和最后一个位置(Find First and Last Position)- LeetCode 34
题目:在排序数组中找到目标值的起始和结束位置,要求O(log n)。
分析:两次二分查找,分别找左边界和右边界。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public int [] searchRange(int [] nums, int target) { return new int []{findBound(nums, target, true ), findBound(nums, target, false )}; } private int findBound (int [] nums, int target, boolean isLeft) { int lo = 0 , hi = nums.length - 1 , result = -1 ; while (lo <= hi) { int mid = lo + (hi - lo) / 2 ; if (nums[mid] == target) { result = mid; if (isLeft) hi = mid - 1 ; else lo = mid + 1 ; } else if (nums[mid] < target) lo = mid + 1 ; else hi = mid - 1 ; } return result; }
时间复杂度:O(log n),空间复杂度:O(1)
18. 🟡 组合总和(Combination Sum)- LeetCode 39
题目:给定无重复元素的数组和目标值,找出所有和为目标值的组合,数字可以重复使用。
分析:回溯法。每次可以选择当前数字(不前进)或跳过当前数字(前进到下一个)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public List<List<Integer>> combinationSum (int [] candidates, int target) { List<List<Integer>> res = new ArrayList <>(); Arrays.sort(candidates); backtrack(res, new ArrayList <>(), candidates, target, 0 ); return res; } private void backtrack (List<List<Integer>> res, List<Integer> path, int [] nums, int remain, int start) { if (remain == 0 ) { res.add(new ArrayList <>(path)); return ; } for (int i = start; i < nums.length && nums[i] <= remain; i++) { path.add(nums[i]); backtrack(res, path, nums, remain - nums[i], i); path.remove(path.size() - 1 ); } }
时间复杂度:O(n^(T/M)),T为target,M为最小候选数,空间复杂度:O(T/M)
19. 🔴 接雨水(Trapping Rain Water)- LeetCode 42
题目:给定n个非负整数表示柱子高度,计算能接多少雨水。
分析:双指针法。每个位置能接的水=min(左边最高, 右边最高) - 当前高度。双指针从两端向中间移动。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public int trap (int [] height) { int left = 0 , right = height.length - 1 ; int leftMax = 0 , rightMax = 0 , water = 0 ; while (left < right) { if (height[left] < height[right]) { leftMax = Math.max(leftMax, height[left]); water += leftMax - height[left]; left++; } else { rightMax = Math.max(rightMax, height[right]); water += rightMax - height[right]; right--; } } return water; }
时间复杂度:O(n),空间复杂度:O(1)
20. 🟡 全排列(Permutations)- LeetCode 46
题目:给定一个不含重复数字的数组,返回其所有可能的全排列。
分析:回溯法经典题。用visited数组标记已使用的数字,递归生成排列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public List<List<Integer>> permute (int [] nums) { List<List<Integer>> res = new ArrayList <>(); backtrack(res, new ArrayList <>(), nums, new boolean [nums.length]); return res; } private void backtrack (List<List<Integer>> res, List<Integer> path, int [] nums, boolean [] used) { if (path.size() == nums.length) { res.add(new ArrayList <>(path)); return ; } for (int i = 0 ; i < nums.length; i++) { if (used[i]) continue ; used[i] = true ; path.add(nums[i]); backtrack(res, path, nums, used); path.remove(path.size() - 1 ); used[i] = false ; } }
时间复杂度:O(n·n!),空间复杂度:O(n)
21. 🟡 旋转图像(Rotate Image)- LeetCode 48
题目:将n×n矩阵顺时针旋转90度,要求原地旋转。
分析:先沿主对角线转置,再每行左右翻转。两步操作等价于顺时针旋转90度。
1 2 3 4 5 6 7 8 9 10 11 12 13 public void rotate (int [][] matrix) { int n = matrix.length; for (int i = 0 ; i < n; i++) for (int j = i + 1 ; j < n; j++) { int tmp = matrix[i][j]; matrix[i][j] = matrix[j][i]; matrix[j][i] = tmp; } for (int i = 0 ; i < n; i++) for (int l = 0 , r = n - 1 ; l < r; l++, r--) { int tmp = matrix[i][l]; matrix[i][l] = matrix[i][r]; matrix[i][r] = tmp; } }
时间复杂度:O(n²),空间复杂度:O(1)
22. 🟡 字母异位词分组(Group Anagrams)- LeetCode 49
题目:给定一个字符串数组,将字母异位词组合在一起。
分析:将每个字符串排序后作为key,相同key的字符串是异位词。也可以用字符计数数组作为key。
1 2 3 4 5 6 7 8 9 10 public List<List<String>> groupAnagrams (String[] strs) { Map<String, List<String>> map = new HashMap <>(); for (String s : strs) { char [] arr = s.toCharArray(); Arrays.sort(arr); String key = new String (arr); map.computeIfAbsent(key, k -> new ArrayList <>()).add(s); } return new ArrayList <>(map.values()); }
时间复杂度:O(n·k·log k),k为字符串最大长度,空间复杂度:O(n·k)
23. 🟡 最大子数组和(Maximum Subarray)- LeetCode 53
题目:找到一个具有最大和的连续子数组,返回其最大和。
分析:Kadane算法。维护当前子数组和,如果和变负则重新开始。动态规划思想:dp[i] = max(nums[i], dp[i-1]+nums[i])。
1 2 3 4 5 6 7 8 public int maxSubArray (int [] nums) { int maxSum = nums[0 ], curSum = nums[0 ]; for (int i = 1 ; i < nums.length; i++) { curSum = Math.max(nums[i], curSum + nums[i]); maxSum = Math.max(maxSum, curSum); } return maxSum; }
时间复杂度:O(n),空间复杂度:O(1)
24. 🟡 跳跃游戏(Jump Game)- LeetCode 55
题目:给定非负整数数组,每个元素代表在该位置可以跳跃的最大长度,判断是否能到达最后一个位置。
分析:贪心。维护能到达的最远位置,遍历过程中如果当前位置超过最远位置则不可达。
1 2 3 4 5 6 7 8 public boolean canJump (int [] nums) { int maxReach = 0 ; for (int i = 0 ; i < nums.length; i++) { if (i > maxReach) return false ; maxReach = Math.max(maxReach, i + nums[i]); } return true ; }
时间复杂度:O(n),空间复杂度:O(1)
25. 🟡 合并区间(Merge Intervals)- LeetCode 56
题目:合并所有重叠的区间。
分析:按起始位置排序,然后逐个合并。如果当前区间的起始≤上一个区间的结束,则合并。
1 2 3 4 5 6 7 8 9 10 11 12 public int [][] merge(int [][] intervals) { Arrays.sort(intervals, (a, b) -> a[0 ] - b[0 ]); List<int []> res = new ArrayList <>(); for (int [] interval : intervals) { if (res.isEmpty() || res.get(res.size() - 1 )[1 ] < interval[0 ]) { res.add(interval); } else { res.get(res.size() - 1 )[1 ] = Math.max(res.get(res.size() - 1 )[1 ], interval[1 ]); } } return res.toArray(new int [0 ][]); }
时间复杂度:O(n·log n),空间复杂度:O(n)
26. 🟡 不同路径(Unique Paths)- LeetCode 62
题目:m×n网格,从左上角到右下角,每次只能向下或向右移动一步,有多少条不同路径。
分析:动态规划。dp[i][j] = dp[i-1][j] + dp[i][j-1]。可以优化为一维数组。
1 2 3 4 5 6 7 8 public int uniquePaths (int m, int n) { int [] dp = new int [n]; Arrays.fill(dp, 1 ); for (int i = 1 ; i < m; i++) for (int j = 1 ; j < n; j++) dp[j] += dp[j - 1 ]; return dp[n - 1 ]; }
时间复杂度:O(m·n),空间复杂度:O(n)
27. 🟡 最小路径和(Minimum Path Sum)- LeetCode 64
题目:m×n网格中每个格子有非负整数,找到从左上角到右下角的路径使得路径上数字总和最小。
分析:动态规划。dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1])。可以原地修改grid节省空间。
1 2 3 4 5 6 7 8 9 public int minPathSum (int [][] grid) { int m = grid.length, n = grid[0 ].length; for (int i = 1 ; i < m; i++) grid[i][0 ] += grid[i - 1 ][0 ]; for (int j = 1 ; j < n; j++) grid[0 ][j] += grid[0 ][j - 1 ]; for (int i = 1 ; i < m; i++) for (int j = 1 ; j < n; j++) grid[i][j] += Math.min(grid[i - 1 ][j], grid[i][j - 1 ]); return grid[m - 1 ][n - 1 ]; }
时间复杂度:O(m·n),空间复杂度:O(1)
28. 🟢 爬楼梯(Climbing Stairs)- LeetCode 70
题目:每次可以爬1或2个台阶,有多少种不同的方法爬到n阶。
分析:斐波那契数列。dp[i] = dp[i-1] + dp[i-2]。只需要两个变量。
1 2 3 4 5 6 public int climbStairs (int n) { if (n <= 2 ) return n; int a = 1 , b = 2 ; for (int i = 3 ; i <= n; i++) { int c = a + b; a = b; b = c; } return b; }
时间复杂度:O(n),空间复杂度:O(1)
29. 🔴 最小覆盖子串(Minimum Window Substring)- LeetCode 76
题目:给定字符串s和t,找出s中包含t所有字符的最小子串。
分析:滑动窗口。右指针扩展直到包含t的所有字符,然后左指针收缩找最小窗口。用数组计数比HashMap快。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public String minWindow (String s, String t) { int [] need = new int [128 ], window = new int [128 ]; for (char c : t.toCharArray()) need[c]++; int left = 0 , count = 0 , minLen = Integer.MAX_VALUE, start = 0 ; int required = 0 ; for (int c : need) if (c > 0 ) required++; for (int right = 0 ; right < s.length(); right++) { char c = s.charAt(right); window[c]++; if (window[c] == need[c]) count++; while (count == required) { if (right - left + 1 < minLen) { minLen = right - left + 1 ; start = left; } char lc = s.charAt(left); if (window[lc] == need[lc]) count--; window[lc]--; left++; } } return minLen == Integer.MAX_VALUE ? "" : s.substring(start, start + minLen); }
时间复杂度:O(m+n),空间复杂度:O(1)
30. 🟡 子集(Subsets)- LeetCode 78
题目:给定一组不含重复元素的整数数组,返回所有可能的子集。
分析:回溯法。每个元素选或不选,也可以用位运算枚举。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public List<List<Integer>> subsets (int [] nums) { List<List<Integer>> res = new ArrayList <>(); backtrack(res, new ArrayList <>(), nums, 0 ); return res; } private void backtrack (List<List<Integer>> res, List<Integer> path, int [] nums, int start) { res.add(new ArrayList <>(path)); for (int i = start; i < nums.length; i++) { path.add(nums[i]); backtrack(res, path, nums, i + 1 ); path.remove(path.size() - 1 ); } }
时间复杂度:O(n·2^n),空间复杂度:O(n)
31. 🟡 单词搜索(Word Search)- LeetCode 79
题目:给定m×n网格和一个单词,判断单词是否存在于网格中(相邻单元格水平或垂直连接)。
分析:DFS回溯。从每个格子出发尝试匹配,用原地修改标记已访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public boolean exist (char [][] board, String word) { for (int i = 0 ; i < board.length; i++) for (int j = 0 ; j < board[0 ].length; j++) if (dfs(board, word, i, j, 0 )) return true ; return false ; } private boolean dfs (char [][] board, String word, int i, int j, int k) { if (k == word.length()) return true ; if (i < 0 || i >= board.length || j < 0 || j >= board[0 ].length || board[i][j] != word.charAt(k)) return false ; char tmp = board[i][j]; board[i][j] = '#' ; boolean found = dfs(board, word, i + 1 , j, k + 1 ) || dfs(board, word, i - 1 , j, k + 1 ) || dfs(board, word, i, j + 1 , k + 1 ) || dfs(board, word, i, j - 1 , k + 1 ); board[i][j] = tmp; return found; }
时间复杂度:O(m·n·3^L),L为单词长度,空间复杂度:O(L)
32. 🔴 柱状图中最大的矩形(Largest Rectangle in Histogram)- LeetCode 84
题目:给定n个非负整数表示柱状图中各柱子的高度,找出能勾勒出的最大矩形面积。
分析:单调栈。维护一个递增栈,遇到更矮的柱子时弹出计算面积。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public int largestRectangleArea (int [] heights) { Deque<Integer> stack = new ArrayDeque <>(); int max = 0 , n = heights.length; for (int i = 0 ; i <= n; i++) { int h = (i == n) ? 0 : heights[i]; while (!stack.isEmpty() && h < heights[stack.peek()]) { int height = heights[stack.pop()]; int width = stack.isEmpty() ? i : i - stack.peek() - 1 ; max = Math.max(max, height * width); } stack.push(i); } return max; }
时间复杂度:O(n),空间复杂度:O(n)
33. 🟡 二叉树的中序遍历(Binary Tree Inorder Traversal)- LeetCode 94
题目:给定二叉树的根节点,返回中序遍历结果。
分析:递归简单,迭代用栈模拟。Morris遍历可以O(1)空间。
1 2 3 4 5 6 7 8 9 10 11 12 public List<Integer> inorderTraversal (TreeNode root) { List<Integer> res = new ArrayList <>(); Deque<TreeNode> stack = new ArrayDeque <>(); TreeNode curr = root; while (curr != null || !stack.isEmpty()) { while (curr != null ) { stack.push(curr); curr = curr.left; } curr = stack.pop(); res.add(curr.val); curr = curr.right; } return res; }
时间复杂度:O(n),空间复杂度:O(n)
34. 🟡 不同的二叉搜索树(Unique Binary Search Trees)- LeetCode 96
题目:给定n,求1到n为节点组成的二叉搜索树有多少种。
分析:卡特兰数。dp[n] = Σ dp[i-1]*dp[n-i],i从1到n。以i为根,左子树i-1个节点,右子树n-i个节点。
1 2 3 4 5 6 7 8 public int numTrees (int n) { int [] dp = new int [n + 1 ]; dp[0 ] = dp[1 ] = 1 ; for (int i = 2 ; i <= n; i++) for (int j = 1 ; j <= i; j++) dp[i] += dp[j - 1 ] * dp[i - j]; return dp[n]; }
时间复杂度:O(n²),空间复杂度:O(n)
35. 🟡 验证二叉搜索树(Validate Binary Search Tree)- LeetCode 98
题目:判断给定二叉树是否是有效的二叉搜索树。
分析:中序遍历应该是严格递增的。也可以递归传递上下界。
1 2 3 4 5 6 7 8 9 public boolean isValidBST (TreeNode root) { return validate(root, Long.MIN_VALUE, Long.MAX_VALUE); } private boolean validate (TreeNode node, long min, long max) { if (node == null ) return true ; if (node.val <= min || node.val >= max) return false ; return validate(node.left, min, node.val) && validate(node.right, node.val, max); }
时间复杂度:O(n),空间复杂度:O(h),h为树高
36. 🟢 对称二叉树(Symmetric Tree)- LeetCode 101
题目:判断二叉树是否是镜像对称的。
分析:递归比较左子树和右子树。左子树的左孩子=右子树的右孩子,左子树的右孩子=右子树的左孩子。
1 2 3 4 5 6 7 8 9 public boolean isSymmetric (TreeNode root) { return root == null || isMirror(root.left, root.right); } private boolean isMirror (TreeNode l, TreeNode r) { if (l == null && r == null ) return true ; if (l == null || r == null ) return false ; return l.val == r.val && isMirror(l.left, r.right) && isMirror(l.right, r.left); }
时间复杂度:O(n),空间复杂度:O(h)
37. 🟡 二叉树的层序遍历(Binary Tree Level Order Traversal)- LeetCode 102
题目:返回二叉树的层序遍历结果(逐层从左到右)。
分析:BFS用队列。每层处理队列中当前所有节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public List<List<Integer>> levelOrder (TreeNode root) { List<List<Integer>> res = new ArrayList <>(); if (root == null ) return res; Queue<TreeNode> queue = new LinkedList <>(); queue.offer(root); while (!queue.isEmpty()) { int size = queue.size(); List<Integer> level = new ArrayList <>(); for (int i = 0 ; i < size; i++) { TreeNode node = queue.poll(); level.add(node.val); if (node.left != null ) queue.offer(node.left); if (node.right != null ) queue.offer(node.right); } res.add(level); } return res; }
时间复杂度:O(n),空间复杂度:O(n)
38. 🟢 二叉树的最大深度(Maximum Depth of Binary Tree)- LeetCode 104
题目:给定二叉树,找出其最大深度。
分析:递归。当前节点的深度 = 1 + max(左子树深度, 右子树深度)。
1 2 3 4 public int maxDepth (TreeNode root) { if (root == null ) return 0 ; return 1 + Math.max(maxDepth(root.left), maxDepth(root.right)); }
时间复杂度:O(n),空间复杂度:O(h)
39. 🟡 从前序与中序遍历序列构造二叉树(Construct Binary Tree)- LeetCode 105
题目:根据前序遍历和中序遍历构造二叉树。
分析:前序第一个元素是根,在中序中找到根的位置,左边是左子树,右边是右子树,递归构建。用HashMap加速查找。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public TreeNode buildTree (int [] preorder, int [] inorder) { Map<Integer, Integer> map = new HashMap <>(); for (int i = 0 ; i < inorder.length; i++) map.put(inorder[i], i); return build(preorder, 0 , preorder.length - 1 , 0 , map); } private TreeNode build (int [] preorder, int preStart, int preEnd, int inStart, Map<Integer, Integer> map) { if (preStart > preEnd) return null ; TreeNode root = new TreeNode (preorder[preStart]); int inRoot = map.get(root.val); int leftSize = inRoot - inStart; root.left = build(preorder, preStart + 1 , preStart + leftSize, inStart, map); root.right = build(preorder, preStart + leftSize + 1 , preEnd, inRoot + 1 , map); return root; }
时间复杂度:O(n),空间复杂度:O(n)
40. 🟡 二叉树展开为链表(Flatten Binary Tree to Linked List)- LeetCode 114
题目:将二叉树展开为单链表(按前序遍历顺序,用right指针连接)。
分析:对每个节点,将左子树插入到当前节点和右子树之间。找到左子树的最右节点连接原右子树。
1 2 3 4 5 6 7 8 9 10 11 12 13 public void flatten (TreeNode root) { TreeNode curr = root; while (curr != null ) { if (curr.left != null ) { TreeNode rightmost = curr.left; while (rightmost.right != null ) rightmost = rightmost.right; rightmost.right = curr.right; curr.right = curr.left; curr.left = null ; } curr = curr.right; } }
时间复杂度:O(n),空间复杂度:O(1)
41. 🔴 买卖股票的最佳时机 III(Best Time to Buy and Sell Stock III)- LeetCode 123
题目:最多可以完成两笔交易,求最大利润。
分析:维护四个状态:第一次买入、第一次卖出、第二次买入、第二次卖出的最大利润。
1 2 3 4 5 6 7 8 9 10 11 public int maxProfit (int [] prices) { int buy1 = Integer.MIN_VALUE, sell1 = 0 ; int buy2 = Integer.MIN_VALUE, sell2 = 0 ; for (int p : prices) { buy1 = Math.max(buy1, -p); sell1 = Math.max(sell1, buy1 + p); buy2 = Math.max(buy2, sell1 - p); sell2 = Math.max(sell2, buy2 + p); } return sell2; }
时间复杂度:O(n),空间复杂度:O(1)
42. 🟢 买卖股票的最佳时机(Best Time to Buy and Sell Stock)- LeetCode 121
题目:只能买卖一次,求最大利润。
分析:遍历时维护最低价格,每天计算当天卖出的利润。
1 2 3 4 5 6 7 8 public int maxProfit (int [] prices) { int minPrice = Integer.MAX_VALUE, maxProfit = 0 ; for (int p : prices) { minPrice = Math.min(minPrice, p); maxProfit = Math.max(maxProfit, p - minPrice); } return maxProfit; }
时间复杂度:O(n),空间复杂度:O(1)
43. 🔴 二叉树中的最大路径和(Binary Tree Maximum Path Sum)- LeetCode 124
题目:给定二叉树,找出路径和最大的路径(路径可以从任意节点到任意节点)。
分析:后序遍历。每个节点计算经过它的最大路径和(左+右+自身),同时向上返回单边最大贡献值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int maxSum = Integer.MIN_VALUE;public int maxPathSum (TreeNode root) { dfs(root); return maxSum; } private int dfs (TreeNode node) { if (node == null ) return 0 ; int left = Math.max(0 , dfs(node.left)); int right = Math.max(0 , dfs(node.right)); maxSum = Math.max(maxSum, left + right + node.val); return node.val + Math.max(left, right); }
时间复杂度:O(n),空间复杂度:O(h)
44. 🟡 最长连续序列(Longest Consecutive Sequence)- LeetCode 128
题目:给定未排序的整数数组,找出最长连续序列的长度,要求O(n)。
分析:HashSet存所有数。对每个数,如果num-1不存在(说明是序列起点),则向后计数。
1 2 3 4 5 6 7 8 9 10 11 12 13 public int longestConsecutive (int [] nums) { Set<Integer> set = new HashSet <>(); for (int n : nums) set.add(n); int max = 0 ; for (int n : set) { if (!set.contains(n - 1 )) { int len = 1 ; while (set.contains(n + len)) len++; max = Math.max(max, len); } } return max; }
时间复杂度:O(n),空间复杂度:O(n)
45. 🟡 单词拆分(Word Break)- LeetCode 139
题目:给定字符串s和字典wordDict,判断s是否可以被拆分为字典中的单词。
分析:动态规划。dp[i]表示s的前i个字符是否可以被拆分。
1 2 3 4 5 6 7 8 9 public boolean wordBreak (String s, List<String> wordDict) { Set<String> dict = 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] && dict.contains(s.substring(j, i))) { dp[i] = true ; break ; } return dp[s.length()]; }
时间复杂度:O(n²·k),k为子串比较时间,空间复杂度:O(n)
46. 🟡 环形链表 II(Linked List Cycle II)- LeetCode 142
题目:给定链表,返回环的入口节点。如果没有环返回null。
分析:快慢指针。快指针每次走2步,慢指针每次走1步。相遇后,一个指针从头开始,两个指针每次走1步,再次相遇点就是环入口。
1 2 3 4 5 6 7 8 9 10 11 12 13 public ListNode detectCycle (ListNode head) { ListNode slow = head, fast = head; while (fast != null && fast.next != null ) { slow = slow.next; fast = fast.next.next; if (slow == fast) { ListNode p = head; while (p != slow) { p = p.next; slow = slow.next; } return p; } } return null ; }
时间复杂度:O(n),空间复杂度:O(1)
47. 🟡 LRU缓存(LRU Cache)- LeetCode 146
题目:设计LRU缓存,支持get和put操作,均为O(1)时间复杂度。
分析:HashMap + 双向链表。HashMap存key到链表节点的映射,双向链表维护访问顺序。面试超高频设计题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class LRUCache { private Map<Integer, Node> map = new HashMap <>(); private Node head = new Node (), tail = new Node (); private int capacity; class Node { int key, val; Node prev, next; Node() {} Node(int k, int v) { key = k; val = v; } } public LRUCache (int capacity) { this .capacity = capacity; head.next = tail; tail.prev = head; } public int get (int key) { Node node = map.get(key); if (node == null ) return -1 ; moveToHead(node); return node.val; } public void put (int key, int value) { Node node = map.get(key); if (node != null ) { node.val = value; moveToHead(node); return ; } node = new Node (key, value); map.put(key, node); addToHead(node); if (map.size() > capacity) { Node tail = removeTail(); map.remove(tail.key); } } private void addToHead (Node n) { n.next = head.next; n.prev = head; head.next.prev = n; head.next = n; } private void remove (Node n) { n.prev.next = n.next; n.next.prev = n.prev; } private void moveToHead (Node n) { remove(n); addToHead(n); } private Node removeTail () { Node n = tail.prev; remove(n); return n; } }
时间复杂度:get/put均O(1),空间复杂度:O(capacity)
48. 🟡 排序链表(Sort List)- LeetCode 148
题目:对链表进行排序,要求O(n log n)时间和O(1)空间。
分析:归并排序。快慢指针找中点,递归分割,合并两个有序链表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public ListNode sortList (ListNode head) { if (head == null || head.next == null ) return head; ListNode slow = head, fast = head.next; while (fast != null && fast.next != null ) { slow = slow.next; fast = fast.next.next; } ListNode mid = slow.next; slow.next = null ; return mergeTwoLists(sortList(head), sortList(mid)); } private ListNode mergeTwoLists (ListNode l1, ListNode l2) { ListNode dummy = new ListNode (0 ), curr = dummy; while (l1 != null && l2 != null ) { if (l1.val <= l2.val) { curr.next = l1; l1 = l1.next; } else { curr.next = l2; l2 = l2.next; } curr = curr.next; } curr.next = (l1 != null ) ? l1 : l2; return dummy.next; }
时间复杂度:O(n·log n),空间复杂度:O(log n)递归栈
49. 🟡 乘积最大子数组(Maximum Product Subarray)- LeetCode 152
题目:找出数组中乘积最大的连续子数组。
分析:维护当前最大值和最小值(负数乘负数可能变最大)。遇到负数时交换最大最小值。
1 2 3 4 5 6 7 8 9 10 public int maxProduct (int [] nums) { int max = nums[0 ], min = nums[0 ], result = nums[0 ]; for (int i = 1 ; i < nums.length; i++) { if (nums[i] < 0 ) { int tmp = max; max = min; min = tmp; } max = Math.max(nums[i], max * nums[i]); min = Math.min(nums[i], min * nums[i]); result = Math.max(result, max); } return result; }
时间复杂度:O(n),空间复杂度:O(1)
50. 🟡 最小栈(Min Stack)- LeetCode 155
题目:设计一个支持push、pop、top和getMin操作的栈,getMin要求O(1)。
分析:用辅助栈同步记录每个状态下的最小值。也可以用一个栈存差值。
1 2 3 4 5 6 7 8 9 10 11 12 13 class MinStack { private Deque<Integer> stack = new ArrayDeque <>(); private Deque<Integer> minStack = new ArrayDeque <>(); public void push (int val) { stack.push(val); minStack.push(minStack.isEmpty() ? val : Math.min(val, minStack.peek())); } public void pop () { stack.pop(); minStack.pop(); } public int top () { return stack.peek(); } public int getMin () { return minStack.peek(); } }
时间复杂度:所有操作O(1),空间复杂度:O(n)
51. 🟡 相交链表(Intersection of Two Linked Lists)- LeetCode 160
题目:找到两个单链表相交的起始节点。
分析:双指针。A走完走B,B走完走A,两者走的总长度相同,会在交点相遇。
1 2 3 4 5 6 7 8 public ListNode getIntersectionNode (ListNode headA, ListNode headB) { ListNode a = headA, b = headB; while (a != b) { a = (a != null ) ? a.next : headB; b = (b != null ) ? b.next : headA; } return a; }
时间复杂度:O(m+n),空间复杂度:O(1)
52. 🟡 多数元素(Majority Element)- LeetCode 169
题目:找出数组中出现次数超过n/2的元素。
分析:Boyer-Moore投票算法。维护候选人和计数,遇到相同+1,不同-1,计数为0时换候选人。
1 2 3 4 5 6 7 8 public int majorityElement (int [] nums) { int candidate = 0 , count = 0 ; for (int n : nums) { if (count == 0 ) candidate = n; count += (n == candidate) ? 1 : -1 ; } return candidate; }
时间复杂度:O(n),空间复杂度:O(1)
53. 🟡 打家劫舍(House Robber)- LeetCode 198
题目:不能偷相邻的房屋,求能偷到的最大金额。
分析:动态规划。dp[i] = max(dp[i-1], dp[i-2]+nums[i])。只需两个变量。
1 2 3 4 5 public int rob (int [] nums) { int prev = 0 , curr = 0 ; for (int n : nums) { int tmp = curr; curr = Math.max(curr, prev + n); prev = tmp; } return curr; }
时间复杂度:O(n),空间复杂度:O(1)
54. 🟡 岛屿数量(Number of Islands)- LeetCode 200
题目:给定m×n的二维网格(’1’是陆地,’0’是水),计算岛屿数量。
分析:DFS/BFS。遍历网格,遇到’1’就DFS将整个岛标记为已访问,计数+1。
1 2 3 4 5 6 7 8 9 10 11 12 13 public int numIslands (char [][] grid) { int count = 0 ; for (int i = 0 ; i < grid.length; i++) for (int j = 0 ; j < grid[0 ].length; j++) if (grid[i][j] == '1' ) { dfs(grid, i, j); count++; } return count; } private void dfs (char [][] grid, int i, int j) { if (i < 0 || i >= grid.length || j < 0 || j >= grid[0 ].length || grid[i][j] != '1' ) return ; grid[i][j] = '0' ; dfs(grid, i + 1 , j); dfs(grid, i - 1 , j); dfs(grid, i, j + 1 ); dfs(grid, i, j - 1 ); }
时间复杂度:O(m·n),空间复杂度:O(m·n)
55. 🟡 反转链表(Reverse Linked List)- LeetCode 206
题目:反转单链表。
分析:迭代法用三个指针。也可以递归。
1 2 3 4 5 6 7 8 9 10 public ListNode reverseList (ListNode head) { ListNode prev = null , curr = head; while (curr != null ) { ListNode next = curr.next; curr.next = prev; prev = curr; curr = next; } return prev; }
时间复杂度:O(n),空间复杂度:O(1)
56. 🟡 课程表(Course Schedule)- LeetCode 207
题目:n门课程有先修关系,判断是否可能完成所有课程(检测有向图是否有环)。
分析:拓扑排序(BFS/Kahn算法)。计算入度,入度为0的入队,逐步删除边。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public boolean canFinish (int numCourses, int [][] prerequisites) { int [] indegree = new int [numCourses]; List<List<Integer>> graph = new ArrayList <>(); for (int i = 0 ; i < numCourses; i++) graph.add(new ArrayList <>()); for (int [] p : prerequisites) { graph.get(p[1 ]).add(p[0 ]); indegree[p[0 ]]++; } Queue<Integer> queue = new LinkedList <>(); for (int i = 0 ; i < numCourses; i++) if (indegree[i] == 0 ) queue.offer(i); int count = 0 ; while (!queue.isEmpty()) { int course = queue.poll(); count++; for (int next : graph.get(course)) if (--indegree[next] == 0 ) queue.offer(next); } return count == numCourses; }
时间复杂度:O(V+E),空间复杂度:O(V+E)
57. 🟡 实现Trie(前缀树)(Implement Trie)- LeetCode 208
题目:实现Trie,支持insert、search和startsWith操作。
分析:每个节点包含26个子节点指针和一个结束标记。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Trie { private Trie[] children = new Trie [26 ]; private boolean isEnd; public void insert (String word) { Trie node = this ; for (char c : word.toCharArray()) { int idx = c - 'a' ; if (node.children[idx] == null ) node.children[idx] = new Trie (); node = node.children[idx]; } node.isEnd = true ; } public boolean search (String word) { Trie node = searchPrefix(word); return node != null && node.isEnd; } public boolean startsWith (String prefix) { return searchPrefix(prefix) != null ; } private Trie searchPrefix (String s) { Trie node = this ; for (char c : s.toCharArray()) { node = node.children[c - 'a' ]; if (node == null ) return null ; } return node; } }
时间复杂度:insert/search/startsWith均O(m),m为字符串长度
58. 🟡 数组中的第K个最大元素(Kth Largest Element)- LeetCode 215
题目:找出数组中第k个最大的元素。
分析:快速选择算法(QuickSelect),平均O(n)。也可以用最小堆O(n·log k)。
1 2 3 4 5 6 7 8 public int findKthLargest (int [] nums, int k) { PriorityQueue<Integer> pq = new PriorityQueue <>(); for (int n : nums) { pq.offer(n); if (pq.size() > k) pq.poll(); } return pq.peek(); }
时间复杂度:O(n·log k),空间复杂度:O(k)
59. 🟡 最大正方形(Maximal Square)- LeetCode 221
题目:在由’0’和’1’组成的二维矩阵中,找到只包含’1’的最大正方形,返回其面积。
分析:动态规划。dp[i][j]表示以(i,j)为右下角的最大正方形边长。dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1。
1 2 3 4 5 6 7 8 9 10 11 public int maximalSquare (char [][] matrix) { int m = matrix.length, n = matrix[0 ].length, max = 0 ; int [][] dp = new int [m + 1 ][n + 1 ]; for (int i = 1 ; i <= m; i++) for (int j = 1 ; j <= n; j++) if (matrix[i - 1 ][j - 1 ] == '1' ) { dp[i][j] = Math.min(Math.min(dp[i - 1 ][j], dp[i][j - 1 ]), dp[i - 1 ][j - 1 ]) + 1 ; max = Math.max(max, dp[i][j]); } return max * max; }
时间复杂度:O(m·n),空间复杂度:O(m·n)
60. 🟢 翻转二叉树(Invert Binary Tree)- LeetCode 226
题目:翻转一棵二叉树。
分析:递归交换每个节点的左右子树。
1 2 3 4 5 6 7 public TreeNode invertTree (TreeNode root) { if (root == null ) return null ; TreeNode tmp = root.left; root.left = invertTree(root.right); root.right = invertTree(tmp); return root; }
时间复杂度:O(n),空间复杂度:O(h)
61. 🟡 回文链表(Palindrome Linked List)- LeetCode 234
题目:判断链表是否是回文链表,要求O(1)空间。
分析:快慢指针找中点,反转后半部分,逐一比较。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public boolean isPalindrome (ListNode head) { ListNode slow = head, fast = head; while (fast != null && fast.next != null ) { slow = slow.next; fast = fast.next.next; } ListNode rev = reverse(slow); while (rev != null ) { if (head.val != rev.val) return false ; head = head.next; rev = rev.next; } return true ; } private ListNode reverse (ListNode head) { ListNode prev = null ; while (head != null ) { ListNode next = head.next; head.next = prev; prev = head; head = next; } return prev; }
时间复杂度:O(n),空间复杂度:O(1)
62. 🟡 二叉树的最近公共祖先(Lowest Common Ancestor)- LeetCode 236
题目:给定二叉树和两个节点,找到它们的最近公共祖先。
分析:递归。如果当前节点是p或q则返回当前节点。左右子树分别递归,如果左右都非空则当前节点是LCA。
1 2 3 4 5 6 7 public TreeNode lowestCommonAncestor (TreeNode root, TreeNode p, TreeNode q) { if (root == null || root == p || root == q) return root; TreeNode left = lowestCommonAncestor(root.left, p, q); TreeNode right = lowestCommonAncestor(root.right, p, q); if (left != null && right != null ) return root; return left != null ? left : right; }
时间复杂度:O(n),空间复杂度:O(h)
63. 🟡 除自身以外数组的乘积(Product of Array Except Self)- LeetCode 238
题目:给定数组,返回每个位置除自身外所有元素的乘积,不能用除法。
分析:两次遍历。第一次从左到右计算左侧乘积,第二次从右到左乘以右侧乘积。
1 2 3 4 5 6 7 8 9 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 right = 1 ; for (int i = n - 1 ; i >= 0 ; i--) { res[i] *= right; right *= nums[i]; } return res; }
时间复杂度:O(n),空间复杂度:O(1)(输出数组不算)
64. 🟡 滑动窗口最大值(Sliding Window Maximum)- LeetCode 239
题目:给定数组和窗口大小k,返回每个窗口中的最大值。
分析:单调递减双端队列。队列存下标,保持队列中的值递减。
1 2 3 4 5 6 7 8 9 10 11 public int [] maxSlidingWindow(int [] nums, int k) { Deque<Integer> deque = new ArrayDeque <>(); int [] res = new int [nums.length - k + 1 ]; for (int i = 0 ; i < nums.length; i++) { while (!deque.isEmpty() && deque.peekFirst() < i - k + 1 ) deque.pollFirst(); while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) deque.pollLast(); deque.offerLast(i); if (i >= k - 1 ) res[i - k + 1 ] = nums[deque.peekFirst()]; } return res; }
时间复杂度:O(n),空间复杂度:O(k)
65. 🟡 搜索二维矩阵 II(Search a 2D Matrix II)- LeetCode 240
题目:m×n矩阵,每行从左到右递增,每列从上到下递增,搜索目标值。
分析:从右上角开始。当前值大于target则左移,小于target则下移。
1 2 3 4 5 6 7 8 9 public boolean searchMatrix (int [][] matrix, int target) { int row = 0 , col = matrix[0 ].length - 1 ; while (row < matrix.length && col >= 0 ) { if (matrix[row][col] == target) return true ; else if (matrix[row][col] > target) col--; else row++; } return false ; }
时间复杂度:O(m+n),空间复杂度:O(1)
66. 🟡 完全平方数(Perfect Squares)- LeetCode 279
题目:给定正整数n,找到若干个完全平方数使得它们的和等于n,返回最少的数量。
分析:动态规划。dp[i] = min(dp[i - j*j] + 1),j从1到√i。也可以用BFS。
1 2 3 4 5 6 7 8 9 public int numSquares (int n) { int [] dp = new int [n + 1 ]; Arrays.fill(dp, Integer.MAX_VALUE); dp[0 ] = 0 ; for (int i = 1 ; i <= n; i++) for (int j = 1 ; j * j <= i; j++) dp[i] = Math.min(dp[i], dp[i - j * j] + 1 ); return dp[n]; }
时间复杂度:O(n·√n),空间复杂度:O(n)
67. 🟡 移动零(Move Zeroes)- LeetCode 283
题目:将数组中所有0移动到末尾,保持非零元素的相对顺序。
分析:双指针。一个指针记录非零元素应该放的位置,另一个遍历数组。
1 2 3 4 5 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; j++; } }
时间复杂度:O(n),空间复杂度:O(1)
68. 🟡 寻找重复数(Find the Duplicate Number)- LeetCode 287
题目:n+1个整数在[1,n]范围内,只有一个重复数,找出它。不能修改数组,O(1)空间。
分析:Floyd判圈算法(快慢指针)。将数组看作链表(值是next指针),重复数就是环的入口。
1 2 3 4 5 6 7 public int findDuplicate (int [] nums) { int slow = nums[0 ], fast = nums[0 ]; do { slow = nums[slow]; fast = nums[nums[fast]]; } while (slow != fast); slow = nums[0 ]; while (slow != fast) { slow = nums[slow]; fast = nums[fast]; } return slow; }
时间复杂度:O(n),空间复杂度:O(1)
69. 🔴 最长递增子序列(Longest Increasing Subsequence)- LeetCode 300
题目:给定整数数组,找到最长严格递增子序列的长度。
分析:DP是O(n²),贪心+二分可以O(n·log n)。维护一个tails数组,tails[i]是长度为i+1的递增子序列的最小末尾。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public int lengthOfLIS (int [] nums) { int [] tails = new int [nums.length]; int size = 0 ; for (int n : nums) { int lo = 0 , hi = size; while (lo < hi) { int mid = (lo + hi) / 2 ; if (tails[mid] < n) lo = mid + 1 ; else hi = mid; } tails[lo] = n; if (lo == size) size++; } return size; }
时间复杂度:O(n·log n),空间复杂度:O(n)
70. 🟡 零钱兑换(Coin Change)- LeetCode 322
题目:给定不同面额的硬币和总金额,计算凑成总金额所需的最少硬币数。
分析:完全背包问题。dp[i]表示凑成金额i的最少硬币数。
1 2 3 4 5 6 7 8 9 public int coinChange (int [] coins, int amount) { int [] dp = new int [amount + 1 ]; Arrays.fill(dp, amount + 1 ); dp[0 ] = 0 ; for (int i = 1 ; i <= amount; i++) for (int c : coins) if (c <= i) dp[i] = Math.min(dp[i], dp[i - c] + 1 ); return dp[amount] > amount ? -1 : dp[amount]; }
时间复杂度:O(amount·n),空间复杂度:O(amount)
71. 🟡 打家劫舍 III(House Robber III)- LeetCode 337
题目:二叉树形的房屋,不能偷直接相连的两个节点,求最大金额。
分析:树形DP。每个节点返回两个值:偷/不偷当前节点的最大金额。
1 2 3 4 5 6 7 8 9 10 11 12 public int rob (TreeNode root) { int [] res = dfs(root); return Math.max(res[0 ], res[1 ]); } private int [] dfs(TreeNode node) { if (node == null ) return new int []{0 , 0 }; int [] left = dfs(node.left), right = dfs(node.right); int rob = node.val + left[1 ] + right[1 ]; int notRob = Math.max(left[0 ], left[1 ]) + Math.max(right[0 ], right[1 ]); return new int []{rob, notRob}; }
时间复杂度:O(n),空间复杂度:O(h)
72. 🟡 比特位计数(Counting Bits)- LeetCode 338
题目:给定整数n,返回0到n每个数的二进制中1的个数。
分析:动态规划。dp[i] = dp[i >> 1] + (i & 1),即右移一位的结果加上最低位。
1 2 3 4 5 public int [] countBits(int n) { int [] dp = new int [n + 1 ]; for (int i = 1 ; i <= n; i++) dp[i] = dp[i >> 1 ] + (i & 1 ); return dp; }
时间复杂度:O(n),空间复杂度:O(n)
73. 🟡 前K个高频元素(Top K Frequent Elements)- LeetCode 347
题目:给定整数数组和整数k,返回出现频率前k高的元素。
分析:HashMap统计频率,然后用最小堆取前k个。也可以用桶排序O(n)。
1 2 3 4 5 6 7 8 9 10 11 12 public int [] topKFrequent(int [] nums, int k) { Map<Integer, Integer> freq = new HashMap <>(); for (int n : nums) freq.merge(n, 1 , Integer::sum); PriorityQueue<Integer> pq = new PriorityQueue <>((a, b) -> freq.get(a) - freq.get(b)); for (int key : freq.keySet()) { pq.offer(key); if (pq.size() > k) pq.poll(); } int [] res = new int [k]; for (int i = 0 ; i < k; i++) res[i] = pq.poll(); return res; }
时间复杂度:O(n·log k),空间复杂度:O(n)
74. 🟡 字符串解码(Decode String)- LeetCode 394
题目:给定编码字符串如”3[a2[c]]”,返回解码后的字符串”accaccacc”。
分析:用栈。遇到’[‘时将当前字符串和数字入栈,遇到’]’时弹出拼接。
1 2 3 4 5 6 7 8 9 10 11 12 13 public String decodeString (String s) { Deque<StringBuilder> strStack = new ArrayDeque <>(); Deque<Integer> numStack = new ArrayDeque <>(); StringBuilder curr = new StringBuilder (); int num = 0 ; for (char c : s.toCharArray()) { if (Character.isDigit(c)) { num = num * 10 + (c - '0' ); } else if (c == '[' ) { strStack.push(curr); numStack.push(num); curr = new StringBuilder (); num = 0 ; } else if (c == ']' ) { StringBuilder prev = strStack.pop(); int k = numStack.pop(); for (int i = 0 ; i < k; i++) prev.append(curr); curr = prev; } else curr.append(c); } return curr.toString(); }
时间复杂度:O(n·k),k为最大重复次数,空间复杂度:O(n)
75. 🟡 每日温度(Daily Temperatures)- LeetCode 739
题目:给定每日温度数组,返回每天需要等几天才能遇到更高温度。
分析:单调递减栈。栈中存下标,遇到更高温度时弹出并计算天数差。
1 2 3 4 5 6 7 8 9 10 11 12 13 public int [] dailyTemperatures(int [] temperatures) { int n = temperatures.length; int [] res = new int [n]; Deque<Integer> stack = new ArrayDeque <>(); for (int i = 0 ; i < n; i++) { while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) { int idx = stack.pop(); res[idx] = i - idx; } stack.push(i); } return res; }
时间复杂度:O(n),空间复杂度:O(n)
76. 🟡 和为K的子数组(Subarray Sum Equals K)- LeetCode 560
题目:给定整数数组和整数k,找到和为k的连续子数组的个数。
分析:前缀和+HashMap。prefixSum[j] - prefixSum[i] = k,即找之前出现过的前缀和为prefixSum[j]-k的次数。
1 2 3 4 5 6 7 8 9 10 11 public int subarraySum (int [] nums, int k) { Map<Integer, Integer> map = new HashMap <>(); map.put(0 , 1 ); int sum = 0 , count = 0 ; for (int n : nums) { sum += n; count += map.getOrDefault(sum - k, 0 ); map.merge(sum, 1 , Integer::sum); } return count; }
时间复杂度:O(n),空间复杂度:O(n)
77. 🟡 任务调度器(Task Scheduler)- LeetCode 621
题目:给定任务数组和冷却时间n,计算完成所有任务的最短时间。
分析:贪心。最高频任务决定框架,(maxFreq-1)*(n+1)+countOfMax。与总任务数取较大值。
1 2 3 4 5 6 7 8 public int leastInterval (char [] tasks, int n) { int [] freq = new int [26 ]; for (char t : tasks) freq[t - 'A' ]++; int maxFreq = 0 , countMax = 0 ; for (int f : freq) maxFreq = Math.max(maxFreq, f); for (int f : freq) if (f == maxFreq) countMax++; return Math.max(tasks.length, (maxFreq - 1 ) * (n + 1 ) + countMax); }
时间复杂度:O(n),空间复杂度:O(1)
78. 🟡 回文子串(Palindromic Substrings)- LeetCode 647
题目:给定字符串,计算有多少个回文子串。
分析:中心扩展法。每个位置作为中心(奇数和偶数长度),向两边扩展计数。
1 2 3 4 5 6 7 8 9 10 11 12 13 public int countSubstrings (String s) { int count = 0 ; for (int i = 0 ; i < s.length(); i++) { count += expand(s, i, i) + expand(s, i, i + 1 ); } return count; } private int expand (String s, int l, int r) { int count = 0 ; while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) { count++; l--; r++; } return count; }
时间复杂度:O(n²),空间复杂度:O(1)
79. 🟡 目标和(Target Sum)- LeetCode 494
题目:给定数组和目标值,每个数字前可以加+或-,求有多少种方式使得总和等于目标值。
分析:转化为0-1背包。设正数和为P,则P-(S-P)=target,P=(S+target)/2。
1 2 3 4 5 6 7 8 9 10 11 12 public int findTargetSumWays (int [] nums, int target) { int sum = 0 ; for (int n : nums) sum += n; if ((sum + target) % 2 != 0 || sum + target < 0 ) return 0 ; int bag = (sum + target) / 2 ; int [] dp = new int [bag + 1 ]; dp[0 ] = 1 ; for (int n : nums) for (int j = bag; j >= n; j--) dp[j] += dp[j - n]; return dp[bag]; }
时间复杂度:O(n·bag),空间复杂度:O(bag)
80. 🟡 把二叉搜索树转换为累加树(Convert BST to Greater Tree)- LeetCode 538
题目:将BST转换为累加树,每个节点的值改为原树中大于等于该节点值的所有节点值之和。
分析:反向中序遍历(右-根-左),维护累加和。
1 2 3 4 5 6 7 8 9 10 11 private int sum = 0 ;public TreeNode convertBST (TreeNode root) { if (root != null ) { convertBST(root.right); sum += root.val; root.val = sum; convertBST(root.left); } return root; }
时间复杂度:O(n),空间复杂度:O(h)
81. 🟡 二叉树的直径(Diameter of Binary Tree)- LeetCode 543
题目:给定二叉树,计算任意两个节点之间的最长路径长度。
分析:后序遍历。每个节点的直径=左子树深度+右子树深度,取全局最大值。
1 2 3 4 5 6 7 8 9 10 11 12 13 private int diameter = 0 ;public int diameterOfBinaryTree (TreeNode root) { depth(root); return diameter; } private int depth (TreeNode node) { if (node == null ) return 0 ; int left = depth(node.left), right = depth(node.right); diameter = Math.max(diameter, left + right); return 1 + Math.max(left, right); }
时间复杂度:O(n),空间复杂度:O(h)
82. 🟡 合并二叉树(Merge Two Binary Trees)- LeetCode 617
题目:合并两棵二叉树,重叠节点值相加,不重叠的保留。
分析:递归同时遍历两棵树。
1 2 3 4 5 6 7 8 public TreeNode mergeTrees (TreeNode t1, TreeNode t2) { if (t1 == null ) return t2; if (t2 == null ) return t1; t1.val += t2.val; t1.left = mergeTrees(t1.left, t2.left); t1.right = mergeTrees(t1.right, t2.right); return t1; }
时间复杂度:O(min(m,n)),空间复杂度:O(min(h1,h2))
83. 🟡 最短无序连续子数组(Shortest Unsorted Continuous Subarray)- LeetCode 581
题目:找到最短的连续子数组,排序该子数组后整个数组变为升序。
分析:从左找右边界(最后一个小于左侧最大值的位置),从右找左边界(最后一个大于右侧最小值的位置)。
1 2 3 4 5 6 7 8 9 public int findUnsortedSubarray (int [] nums) { int n = nums.length, left = -1 , right = -1 ; int max = Integer.MIN_VALUE, min = Integer.MAX_VALUE; for (int i = 0 ; i < n; i++) { if (nums[i] < max) right = i; else max = nums[i]; if (nums[n - 1 - i] > min) left = n - 1 - i; else min = nums[n - 1 - i]; } return right == -1 ? 0 : right - left + 1 ; }
时间复杂度:O(n),空间复杂度:O(1)
84. 🟢 汉明距离(Hamming Distance)- LeetCode 461
题目:计算两个整数之间的汉明距离(对应位不同的位数)。
分析:异或后统计1的个数。
1 2 3 public int hammingDistance (int x, int y) { return Integer.bitCount(x ^ y); }
时间复杂度:O(1),空间复杂度:O(1)
85. 🟡 找到所有数组中消失的数字(Find All Numbers Disappeared)- LeetCode 448
题目:n个整数在[1,n]范围内,找出所有未出现的数字,不用额外空间。
分析:原地标记。将nums[abs(nums[i])-1]取负,最后正数对应的下标+1就是缺失的数。
1 2 3 4 5 6 public List<Integer> findDisappearedNumbers (int [] nums) { for (int n : nums) { int idx = Math.abs(n) - 1 ; if (nums[idx] > 0 ) nums[idx] = -nums[idx]; } List<Integer> res = new ArrayList <>(); for (int i = 0 ; i < nums.length; i++) if (nums[i] > 0 ) res.add(i + 1 ); return res; }
时间复杂度:O(n),空间复杂度:O(1)
86. 🟡 分割等和子集(Partition Equal Subset Sum)- LeetCode 416
题目:判断数组是否可以分割为两个子集,使得两个子集的元素和相等。
分析:0-1背包。目标和为sum/2,dp[j]表示是否能凑出和为j。
1 2 3 4 5 6 7 8 9 10 11 12 public boolean canPartition (int [] nums) { int sum = 0 ; for (int n : nums) sum += n; if (sum % 2 != 0 ) return false ; int target = sum / 2 ; boolean [] dp = new boolean [target + 1 ]; dp[0 ] = true ; for (int n : nums) for (int j = target; j >= n; j--) dp[j] = dp[j] || dp[j - n]; return dp[target]; }
时间复杂度:O(n·target),空间复杂度:O(target)
87. 🟡 路径总和 III(Path Sum III)- LeetCode 437
题目:二叉树中找出路径和等于目标值的路径数量(路径不需要从根开始或到叶子结束)。
分析:前缀和+HashMap。类似子数组和为K的思路,在树上用DFS+回溯。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public int pathSum (TreeNode root, int targetSum) { Map<Long, Integer> prefix = new HashMap <>(); prefix.put(0L , 1 ); return dfs(root, 0 , targetSum, prefix); } private int dfs (TreeNode node, long currSum, int target, Map<Long, Integer> prefix) { if (node == null ) return 0 ; currSum += node.val; int count = prefix.getOrDefault(currSum - target, 0 ); prefix.merge(currSum, 1 , Integer::sum); count += dfs(node.left, currSum, target, prefix) + dfs(node.right, currSum, target, prefix); prefix.merge(currSum, -1 , Integer::sum); return count; }
时间复杂度:O(n),空间复杂度:O(h)
88. 🟡 找到字符串中所有字母异位词(Find All Anagrams)- LeetCode 438
题目:给定字符串s和p,找到s中所有p的字母异位词的起始索引。
分析:固定长度滑动窗口+字符计数数组比较。
1 2 3 4 5 6 7 8 9 10 11 12 public List<Integer> findAnagrams (String s, String p) { List<Integer> res = new ArrayList <>(); if (s.length() < p.length()) return res; int [] pCount = new int [26 ], sCount = new int [26 ]; for (char c : p.toCharArray()) pCount[c - 'a' ]++; for (int i = 0 ; i < s.length(); i++) { sCount[s.charAt(i) - 'a' ]++; if (i >= p.length()) sCount[s.charAt(i - p.length()) - 'a' ]--; if (Arrays.equals(sCount, pCount)) res.add(i - p.length() + 1 ); } return res; }
时间复杂度:O(n),空间复杂度:O(1)
89. 🟡 二叉树的右视图(Binary Tree Right Side View)- LeetCode 199
题目:给定二叉树,返回从右侧看到的节点值。
分析:BFS层序遍历,每层取最后一个节点。或DFS先访问右子树。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public List<Integer> rightSideView (TreeNode root) { List<Integer> res = new ArrayList <>(); if (root == null ) return res; Queue<TreeNode> queue = new LinkedList <>(); queue.offer(root); while (!queue.isEmpty()) { int size = queue.size(); for (int i = 0 ; i < size; i++) { TreeNode node = queue.poll(); if (i == size - 1 ) res.add(node.val); if (node.left != null ) queue.offer(node.left); if (node.right != null ) queue.offer(node.right); } } return res; }
时间复杂度:O(n),空间复杂度:O(n)
90. 🟡 螺旋矩阵(Spiral Matrix)- LeetCode 54
题目:给定m×n矩阵,按螺旋顺序返回所有元素。
分析:模拟。维护上下左右四个边界,按右→下→左→上的顺序遍历,每次遍历完收缩边界。
1 2 3 4 5 6 7 8 9 10 11 public List<Integer> spiralOrder (int [][] matrix) { List<Integer> res = new ArrayList <>(); int top = 0 , bottom = matrix.length - 1 , left = 0 , right = matrix[0 ].length - 1 ; while (top <= bottom && left <= right) { for (int j = left; j <= right; j++) res.add(matrix[top][j]); top++; for (int i = top; i <= bottom; i++) res.add(matrix[i][right]); right--; if (top <= bottom) { for (int j = right; j >= left; j--) res.add(matrix[bottom][j]); bottom--; } if (left <= right) { for (int i = bottom; i >= top; i--) res.add(matrix[i][left]); left++; } } return res; }
时间复杂度:O(m·n),空间复杂度:O(1)
91. 🟡 螺旋矩阵 II(Spiral Matrix II)- LeetCode 59
题目:生成n×n矩阵,按螺旋顺序填入1到n²。
分析:与螺旋矩阵类似,按方向填充。
1 2 3 4 5 6 7 8 9 10 11 public int [][] generateMatrix(int n) { int [][] matrix = new int [n][n]; int top = 0 , bottom = n - 1 , left = 0 , right = n - 1 , num = 1 ; while (top <= bottom && left <= right) { for (int j = left; j <= right; j++) matrix[top][j] = num++; top++; for (int i = top; i <= bottom; i++) matrix[i][right] = num++; right--; for (int j = right; j >= left; j--) matrix[bottom][j] = num++; bottom--; for (int i = bottom; i >= top; i--) matrix[i][left] = num++; left++; } return matrix; }
时间复杂度:O(n²),空间复杂度:O(1)
92. 🟡 颜色分类(Sort Colors)- LeetCode 75
题目:给定包含0、1、2的数组,原地排序(荷兰国旗问题)。
分析:三指针。p0指向0的右边界,p2指向2的左边界,curr遍历。
1 2 3 4 5 6 7 8 9 10 public void sortColors (int [] nums) { int p0 = 0 , curr = 0 , p2 = nums.length - 1 ; while (curr <= p2) { if (nums[curr] == 0 ) { swap(nums, p0++, curr++); } else if (nums[curr] == 2 ) { swap(nums, curr, p2--); } else curr++; } } private void swap (int [] nums, int i, int j) { int t = nums[i]; nums[i] = nums[j]; nums[j] = t; }
时间复杂度:O(n),空间复杂度:O(1)
93. 🟡 编辑距离(Edit Distance)- LeetCode 72
题目:给定两个单词,计算将word1转换为word2所需的最少操作数(插入、删除、替换)。
分析:经典二维DP。dp[i][j]表示word1前i个字符转换为word2前j个字符的最少操作数。
1 2 3 4 5 6 7 8 9 10 11 public int minDistance (String word1, String word2) { int m = word1.length(), n = word2.length(); int [][] dp = new int [m + 1 ][n + 1 ]; for (int i = 0 ; i <= m; i++) dp[i][0 ] = i; for (int j = 0 ; j <= n; j++) dp[0 ][j] = j; for (int i = 1 ; i <= m; i++) for (int j = 1 ; j <= n; j++) if (word1.charAt(i - 1 ) == word2.charAt(j - 1 )) dp[i][j] = dp[i - 1 ][j - 1 ]; else dp[i][j] = 1 + Math.min(dp[i - 1 ][j - 1 ], Math.min(dp[i - 1 ][j], dp[i][j - 1 ])); return dp[m][n]; }
时间复杂度:O(m·n),空间复杂度:O(m·n)
94. 🟡 最长公共子序列(Longest Common Subsequence)- LeetCode 1143
题目:给定两个字符串,返回最长公共子序列的长度。
分析:二维DP。dp[i][j]表示text1前i个和text2前j个字符的LCS长度。
1 2 3 4 5 6 7 8 9 public int longestCommonSubsequence (String text1, String text2) { int m = text1.length(), n = text2.length(); int [][] dp = new int [m + 1 ][n + 1 ]; for (int i = 1 ; i <= m; i++) for (int j = 1 ; j <= n; j++) dp[i][j] = (text1.charAt(i - 1 ) == text2.charAt(j - 1 )) ? dp[i - 1 ][j - 1 ] + 1 : Math.max(dp[i - 1 ][j], dp[i][j - 1 ]); return dp[m][n]; }
时间复杂度:O(m·n),空间复杂度:O(m·n)
95. 🟡 旋转链表(Rotate List)- LeetCode 61
题目:给定链表和k,将链表向右旋转k个位置。
分析:先连成环,然后在正确位置断开。
1 2 3 4 5 6 7 8 9 10 11 12 13 public ListNode rotateRight (ListNode head, int k) { if (head == null || head.next == null || k == 0 ) return head; int len = 1 ; ListNode tail = head; while (tail.next != null ) { tail = tail.next; len++; } k %= len; if (k == 0 ) return head; tail.next = head; for (int i = 0 ; i < len - k; i++) tail = tail.next; head = tail.next; tail.next = null ; return head; }
时间复杂度:O(n),空间复杂度:O(1)
96. 🟡 矩阵置零(Set Matrix Zeroes)- LeetCode 73
题目:如果矩阵中某个元素为0,则将其所在行和列全部置为0,要求原地。
分析:用第一行和第一列作为标记。先记录第一行和第一列是否有0,然后用它们标记其他行列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public void setZeroes (int [][] matrix) { int m = matrix.length, n = matrix[0 ].length; boolean firstRow = false , firstCol = false ; for (int j = 0 ; j < n; j++) if (matrix[0 ][j] == 0 ) firstRow = true ; for (int i = 0 ; i < m; i++) if (matrix[i][0 ] == 0 ) firstCol = true ; 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 (firstRow) for (int j = 0 ; j < n; j++) matrix[0 ][j] = 0 ; if (firstCol) for (int i = 0 ; i < m; i++) matrix[i][0 ] = 0 ; }
时间复杂度:O(m·n),空间复杂度:O(1)
97. 🟡 不同的二叉搜索树 II(Unique BSTs II)- LeetCode 95
题目:给定n,生成所有由1到n为节点组成的不同BST。
分析:递归。以每个数为根,左边的数构成左子树,右边的数构成右子树。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public List<TreeNode> generateTrees (int n) { return build(1 , n); } private List<TreeNode> build (int lo, int hi) { List<TreeNode> res = new ArrayList <>(); if (lo > hi) { res.add(null ); return res; } for (int i = lo; i <= hi; i++) { for (TreeNode left : build(lo, i - 1 )) for (TreeNode right : build(i + 1 , hi)) { TreeNode root = new TreeNode (i); root.left = left; root.right = right; res.add(root); } } return res; }
时间复杂度:O(4^n/√n)(卡特兰数),空间复杂度:O(4^n/√n)
98. 🟡 二叉树的锯齿形层序遍历(Zigzag Level Order Traversal)- LeetCode 103
题目:返回二叉树的锯齿形层序遍历(先左到右,再右到左,交替进行)。
分析:BFS层序遍历,奇数层反转结果。或用双端队列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public List<List<Integer>> zigzagLevelOrder (TreeNode root) { List<List<Integer>> res = new ArrayList <>(); if (root == null ) return res; Queue<TreeNode> queue = new LinkedList <>(); queue.offer(root); boolean leftToRight = true ; while (!queue.isEmpty()) { int size = queue.size(); LinkedList<Integer> level = new LinkedList <>(); for (int i = 0 ; i < size; i++) { TreeNode node = queue.poll(); if (leftToRight) level.addLast(node.val); else level.addFirst(node.val); if (node.left != null ) queue.offer(node.left); if (node.right != null ) queue.offer(node.right); } res.add(level); leftToRight = !leftToRight; } return res; }
时间复杂度:O(n),空间复杂度:O(n)
99. 🟡 从中序与后序遍历序列构造二叉树(Construct Binary Tree II)- LeetCode 106
题目:根据中序遍历和后序遍历构造二叉树。
分析:后序最后一个元素是根,在中序中找到根的位置,递归构建。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public TreeNode buildTree (int [] inorder, int [] postorder) { Map<Integer, Integer> map = new HashMap <>(); for (int i = 0 ; i < inorder.length; i++) map.put(inorder[i], i); return build(postorder, 0 , postorder.length - 1 , 0 , map); } private TreeNode build (int [] postorder, int postStart, int postEnd, int inStart, Map<Integer, Integer> map) { if (postStart > postEnd) return null ; TreeNode root = new TreeNode (postorder[postEnd]); int inRoot = map.get(root.val); int leftSize = inRoot - inStart; root.left = build(postorder, postStart, postStart + leftSize - 1 , inStart, map); root.right = build(postorder, postStart + leftSize, postEnd - 1 , inRoot + 1 , map); return root; }
时间复杂度:O(n),空间复杂度:O(n)
100. 🟡 二叉树的序列化与反序列化(Serialize and Deserialize Binary Tree)- LeetCode 297
题目:设计算法实现二叉树的序列化与反序列化。
分析:BFS层序遍历序列化,null用”#”表示。反序列化时用队列重建。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class Codec { public String serialize (TreeNode root) { if (root == null ) return "#" ; StringBuilder sb = new StringBuilder (); Queue<TreeNode> queue = new LinkedList <>(); queue.offer(root); while (!queue.isEmpty()) { TreeNode node = queue.poll(); if (node == null ) { sb.append("#," ); continue ; } sb.append(node.val).append("," ); queue.offer(node.left); queue.offer(node.right); } return sb.toString(); } public TreeNode deserialize (String data) { if (data.equals("#" )) return null ; String[] vals = data.split("," ); TreeNode root = new TreeNode (Integer.parseInt(vals[0 ])); Queue<TreeNode> queue = new LinkedList <>(); queue.offer(root); int i = 1 ; while (!queue.isEmpty()) { TreeNode node = queue.poll(); if (!vals[i].equals("#" )) { node.left = new TreeNode (Integer.parseInt(vals[i])); queue.offer(node.left); } i++; if (!vals[i].equals("#" )) { node.right = new TreeNode (Integer.parseInt(vals[i])); queue.offer(node.right); } i++; } return root; } }
时间复杂度:O(n),空间复杂度:O(n)
101. 🟡 最长重复子数组(Maximum Length of Repeated Subarray)- LeetCode 718
题目:给定两个整数数组,返回两个数组中公共的、长度最长的子数组的长度。
分析:二维DP。dp[i][j]表示以nums1[i-1]和nums2[j-1]结尾的最长公共子数组长度。
1 2 3 4 5 6 7 8 public int findLength (int [] nums1, int [] nums2) { int m = nums1.length, n = nums2.length, max = 0 ; int [][] dp = new int [m + 1 ][n + 1 ]; for (int i = 1 ; i <= m; i++) for (int j = 1 ; j <= n; j++) if (nums1[i - 1 ] == nums2[j - 1 ]) { dp[i][j] = dp[i - 1 ][j - 1 ] + 1 ; max = Math.max(max, dp[i][j]); } return max; }
时间复杂度:O(m·n),空间复杂度:O(m·n)
102. 🟡 在排序数组中查找元素(Binary Search)- LeetCode 704
题目:标准二分查找。
分析:基础二分,注意边界条件。
1 2 3 4 5 6 7 8 9 10 public int search (int [] nums, int target) { int lo = 0 , hi = nums.length - 1 ; while (lo <= hi) { int mid = lo + (hi - lo) / 2 ; if (nums[mid] == target) return mid; else if (nums[mid] < target) lo = mid + 1 ; else hi = mid - 1 ; } return -1 ; }
时间复杂度:O(log n),空间复杂度:O(1)
103. 🟡 腐烂的橘子(Rotting Oranges)- LeetCode 994
题目:网格中1是新鲜橘子,2是腐烂橘子,每分钟腐烂橘子会感染相邻的新鲜橘子。求所有橘子腐烂的最短时间。
分析:多源BFS。所有腐烂橘子同时入队,逐层扩散。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public int orangesRotting (int [][] grid) { int m = grid.length, n = grid[0 ].length, fresh = 0 , time = 0 ; Queue<int []> queue = new LinkedList <>(); for (int i = 0 ; i < m; i++) for (int j = 0 ; j < n; j++) { if (grid[i][j] == 2 ) queue.offer(new int []{i, j}); else if (grid[i][j] == 1 ) fresh++; } int [][] dirs = {{1 ,0 },{-1 ,0 },{0 ,1 },{0 ,-1 }}; while (!queue.isEmpty() && fresh > 0 ) { int size = queue.size(); time++; for (int k = 0 ; k < size; k++) { int [] cell = queue.poll(); for (int [] d : dirs) { int ni = cell[0 ] + d[0 ], nj = cell[1 ] + d[1 ]; if (ni >= 0 && ni < m && nj >= 0 && nj < n && grid[ni][nj] == 1 ) { grid[ni][nj] = 2 ; fresh--; queue.offer(new int []{ni, nj}); } } } } return fresh == 0 ? time : -1 ; }
时间复杂度:O(m·n),空间复杂度:O(m·n)
104. 🟡 K个一组翻转链表(Reverse Nodes in k-Group)- LeetCode 25
题目:每k个节点一组进行翻转,不足k个的保持原样。
分析:先计算链表长度,然后每k个一组翻转。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public ListNode reverseKGroup (ListNode head, int k) { ListNode dummy = new ListNode (0 , head); ListNode prevGroup = dummy; int len = 0 ; for (ListNode n = head; n != null ; n = n.next) len++; while (len >= k) { ListNode curr = prevGroup.next, prev = null ; for (int i = 0 ; i < k; i++) { ListNode next = curr.next; curr.next = prev; prev = curr; curr = next; } ListNode tail = prevGroup.next; tail.next = curr; prevGroup.next = prev; prevGroup = tail; len -= k; } return dummy.next; }
时间复杂度:O(n),空间复杂度:O(1)
105. 🟡 最长回文子序列(Longest Palindromic Subsequence)- LeetCode 516
题目:给定字符串,找出最长回文子序列的长度。
分析:区间DP。dp[i][j]表示s[i..j]中最长回文子序列长度。
1 2 3 4 5 6 7 8 9 10 public int longestPalindromeSubseq (String s) { int n = s.length(); int [][] dp = new int [n][n]; for (int i = n - 1 ; i >= 0 ; i--) { dp[i][i] = 1 ; for (int j = i + 1 ; j < n; j++) dp[i][j] = (s.charAt(i) == s.charAt(j)) ? dp[i + 1 ][j - 1 ] + 2 : Math.max(dp[i + 1 ][j], dp[i][j - 1 ]); } return dp[0 ][n - 1 ]; }
时间复杂度:O(n²),空间复杂度:O(n²)
106. 🟡 复原IP地址(Restore IP Addresses)- LeetCode 93
题目:给定只包含数字的字符串,返回所有可能的有效IP地址。
分析:回溯法。每段1-3位数字,值在0-255之间,不能有前导零。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public List<String> restoreIpAddresses (String s) { List<String> res = new ArrayList <>(); backtrack(res, new ArrayList <>(), s, 0 ); return res; } private void backtrack (List<String> res, List<String> parts, String s, int start) { if (parts.size() == 4 ) { if (start == s.length()) res.add(String.join("." , parts)); return ; } for (int len = 1 ; len <= 3 && start + len <= s.length(); len++) { String seg = s.substring(start, start + len); if ((seg.length() > 1 && seg.startsWith("0" )) || Integer.parseInt(seg) > 255 ) continue ; parts.add(seg); backtrack(res, parts, s, start + len); parts.remove(parts.size() - 1 ); } }
时间复杂度:O(3^4)=O(1),空间复杂度:O(1)
107. 🟡 单词搜索 II(Word Search II)- LeetCode 212
题目:给定m×n网格和单词列表,找出所有同时在网格和列表中出现的单词。
分析:Trie+DFS。将所有单词构建Trie,然后在网格上DFS匹配。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public List<String> findWords (char [][] board, String[] words) { TrieNode root = new TrieNode (); for (String w : words) { TrieNode node = root; for (char c : w.toCharArray()) { if (node.children[c-'a' ]==null ) node.children[c-'a' ]=new TrieNode (); node=node.children[c-'a' ]; } node.word=w; } List<String> res = new ArrayList <>(); for (int i = 0 ; i < board.length; i++) for (int j = 0 ; j < board[0 ].length; j++) dfs(board, i, j, root, res); return res; } private void dfs (char [][] board, int i, int j, TrieNode node, List<String> res) { if (i<0 ||i>=board.length||j<0 ||j>=board[0 ].length||board[i][j]=='#' ) return ; char c = board[i][j]; if (node.children[c-'a' ]==null ) return ; node = node.children[c-'a' ]; if (node.word!=null ) { res.add(node.word); node.word=null ; } board[i][j]='#' ; dfs(board,i+1 ,j,node,res); dfs(board,i-1 ,j,node,res); dfs(board,i,j+1 ,node,res); dfs(board,i,j-1 ,node,res); board[i][j]=c; } class TrieNode { TrieNode[] children=new TrieNode [26 ]; String word; }
时间复杂度:O(m·n·4^L),空间复杂度:O(总字符数)
108. 🟡 数据流的中位数(Find Median from Data Stream)- LeetCode 295
题目:设计数据结构支持从数据流中添加数字和查找中位数。
分析:大顶堆存较小的一半,小顶堆存较大的一半。保持两个堆大小差不超过1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class MedianFinder { private PriorityQueue<Integer> maxHeap = new PriorityQueue <>(Collections.reverseOrder()); private PriorityQueue<Integer> minHeap = new PriorityQueue <>(); public void addNum (int num) { maxHeap.offer(num); minHeap.offer(maxHeap.poll()); if (minHeap.size() > maxHeap.size()) maxHeap.offer(minHeap.poll()); } public double findMedian () { return maxHeap.size() > minHeap.size() ? maxHeap.peek() : (maxHeap.peek() + minHeap.peek()) / 2.0 ; } }
时间复杂度:addNum O(log n),findMedian O(1)
109. 🟡 课程表 II(Course Schedule II)- LeetCode 210
题目:返回完成所有课程的学习顺序(拓扑排序结果)。
分析:与课程表I类似,BFS拓扑排序,记录出队顺序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public int [] findOrder(int numCourses, int [][] prerequisites) { int [] indegree = new int [numCourses]; List<List<Integer>> graph = new ArrayList <>(); for (int i = 0 ; i < numCourses; i++) graph.add(new ArrayList <>()); for (int [] p : prerequisites) { graph.get(p[1 ]).add(p[0 ]); indegree[p[0 ]]++; } Queue<Integer> queue = new LinkedList <>(); for (int i = 0 ; i < numCourses; i++) if (indegree[i] == 0 ) queue.offer(i); int [] order = new int [numCourses]; int idx = 0 ; while (!queue.isEmpty()) { int course = queue.poll(); order[idx++] = course; for (int next : graph.get(course)) if (--indegree[next] == 0 ) queue.offer(next); } return idx == numCourses ? order : new int [0 ]; }
时间复杂度:O(V+E),空间复杂度:O(V+E)
110. 🟡 戳气球(Burst Balloons)- LeetCode 312
题目:n个气球,戳破第i个气球获得nums[i-1]*nums[i]*nums[i+1]硬币,求最大硬币数。
分析:区间DP。dp[i][j]表示戳破(i,j)开区间内所有气球的最大硬币数。枚举最后一个被戳破的气球k。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public int maxCoins (int [] nums) { int n = nums.length; int [] val = new int [n + 2 ]; val[0 ] = val[n + 1 ] = 1 ; for (int i = 0 ; i < n; i++) val[i + 1 ] = nums[i]; int [][] dp = new int [n + 2 ][n + 2 ]; for (int len = 1 ; len <= n; len++) for (int i = 1 ; i + len - 1 <= n; i++) { int j = i + len - 1 ; for (int k = i; k <= j; k++) dp[i][j] = Math.max(dp[i][j], dp[i][k - 1 ] + val[i - 1 ] * val[k] * val[j + 1 ] + dp[k + 1 ][j]); } return dp[1 ][n]; }
时间复杂度:O(n³),空间复杂度:O(n²)
111. 🟡 除法求值(Evaluate Division)- LeetCode 399
题目:给定变量对之间的除法关系,回答查询。
分析:构建有向加权图,BFS/DFS查找路径,路径上的权重相乘。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public double [] calcEquation(List<List<String>> equations, double [] values, List<List<String>> queries) { Map<String, Map<String, Double>> graph = new HashMap <>(); for (int i = 0 ; i < equations.size(); i++) { String a = equations.get(i).get(0 ), b = equations.get(i).get(1 ); graph.computeIfAbsent(a, k -> new HashMap <>()).put(b, values[i]); graph.computeIfAbsent(b, k -> new HashMap <>()).put(a, 1.0 / values[i]); } double [] res = new double [queries.size()]; for (int i = 0 ; i < queries.size(); i++) { String src = queries.get(i).get(0 ), dst = queries.get(i).get(1 ); if (!graph.containsKey(src) || !graph.containsKey(dst)) { res[i] = -1.0 ; continue ; } res[i] = bfs(graph, src, dst); } return res; } private double bfs (Map<String, Map<String, Double>> graph, String src, String dst) { if (src.equals(dst)) return 1.0 ; Queue<String[]> queue = new LinkedList <>(); Set<String> visited = new HashSet <>(); Queue<Double> products = new LinkedList <>(); queue.offer(new String []{src}); products.offer(1.0 ); visited.add(src); while (!queue.isEmpty()) { String node = queue.poll()[0 ]; double product = products.poll(); for (var entry : graph.get(node).entrySet()) { if (entry.getKey().equals(dst)) return product * entry.getValue(); if (visited.add(entry.getKey())) { queue.offer(new String []{entry.getKey()}); products.offer(product * entry.getValue()); } } } return -1.0 ; }
时间复杂度:O(Q·(V+E)),空间复杂度:O(V+E)
112. 🟡 根据身高重建队列(Queue Reconstruction by Height)- LeetCode 406
题目:每个人有(h,k),h是身高,k是前面身高≥h的人数。重建队列。
分析:贪心。按身高降序、k升序排序,然后按k值插入到对应位置。
1 2 3 4 5 6 public int [][] reconstructQueue(int [][] people) { Arrays.sort(people, (a, b) -> a[0 ] == b[0 ] ? a[1 ] - b[1 ] : b[0 ] - a[0 ]); List<int []> res = new ArrayList <>(); for (int [] p : people) res.add(p[1 ], p); return res.toArray(new int [0 ][]); }
时间复杂度:O(n²)(插入操作),空间复杂度:O(n)
113. 🟡 正则表达式匹配(Regular Expression Matching)- LeetCode 10
题目:实现支持’.’和’*’的正则表达式匹配。
分析:动态规划。dp[i][j]表示s的前i个字符和p的前j个字符是否匹配。’*’可以匹配0个或多个前面的字符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public boolean isMatch (String s, String p) { int m = s.length(), n = p.length(); boolean [][] dp = new boolean [m + 1 ][n + 1 ]; dp[0 ][0 ] = true ; for (int j = 2 ; j <= n; j++) if (p.charAt(j - 1 ) == '*' ) dp[0 ][j] = dp[0 ][j - 2 ]; for (int i = 1 ; i <= m; i++) for (int j = 1 ; j <= n; j++) { char pc = p.charAt(j - 1 ); if (pc == '*' ) { dp[i][j] = dp[i][j - 2 ]; if (p.charAt(j - 2 ) == '.' || p.charAt(j - 2 ) == s.charAt(i - 1 )) dp[i][j] |= dp[i - 1 ][j]; } else { dp[i][j] = dp[i - 1 ][j - 1 ] && (pc == '.' || pc == s.charAt(i - 1 )); } } return dp[m][n]; }
时间复杂度:O(m·n),空间复杂度:O(m·n)
114. 🟡 通配符匹配(Wildcard Matching)- LeetCode 44
题目:实现支持’?’和’‘的通配符匹配。’ ‘匹配任意字符序列(包括空)。
分析:动态规划。与正则匹配类似但’*’语义不同。
1 2 3 4 5 6 7 8 9 10 11 12 public boolean isMatch (String s, String p) { int m = s.length(), n = p.length(); boolean [][] dp = new boolean [m + 1 ][n + 1 ]; dp[0 ][0 ] = true ; for (int j = 1 ; j <= n; j++) if (p.charAt(j - 1 ) == '*' ) dp[0 ][j] = dp[0 ][j - 1 ]; for (int i = 1 ; i <= m; i++) for (int j = 1 ; j <= n; j++) { if (p.charAt(j - 1 ) == '*' ) dp[i][j] = dp[i - 1 ][j] || dp[i][j - 1 ]; else dp[i][j] = dp[i - 1 ][j - 1 ] && (p.charAt(j - 1 ) == '?' || p.charAt(j - 1 ) == s.charAt(i - 1 )); } return dp[m][n]; }
时间复杂度:O(m·n),空间复杂度:O(m·n)
115. 🟡 最大矩形(Maximal Rectangle)- LeetCode 85
题目:给定只包含0和1的二维矩阵,找出只包含1的最大矩形面积。
分析:将每行看作柱状图的底,转化为柱状图最大矩形问题(LeetCode 84)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public int maximalRectangle (char [][] matrix) { if (matrix.length == 0 ) return 0 ; int n = matrix[0 ].length, max = 0 ; int [] heights = new int [n]; for (char [] row : matrix) { for (int j = 0 ; j < n; j++) heights[j] = row[j] == '1' ? heights[j] + 1 : 0 ; max = Math.max(max, largestRectangleArea(heights)); } return max; } private int largestRectangleArea (int [] heights) { Deque<Integer> stack = new ArrayDeque <>(); int max = 0 ; for (int i = 0 ; i <= heights.length; i++) { int h = (i == heights.length) ? 0 : heights[i]; while (!stack.isEmpty() && h < heights[stack.peek()]) { int height = heights[stack.pop()]; int width = stack.isEmpty() ? i : i - stack.peek() - 1 ; max = Math.max(max, height * width); } stack.push(i); } return max; }
时间复杂度:O(m·n),空间复杂度:O(n)
116. 🟡 跳跃游戏 II(Jump Game II)- LeetCode 45
题目:给定非负整数数组,每个元素代表最大跳跃长度,求到达最后位置的最少跳跃次数。
分析:贪心BFS。每次跳跃选择能到达最远的位置。
1 2 3 4 5 6 7 8 public int jump (int [] nums) { int jumps = 0 , curEnd = 0 , farthest = 0 ; for (int i = 0 ; i < nums.length - 1 ; i++) { farthest = Math.max(farthest, i + nums[i]); if (i == curEnd) { jumps++; curEnd = farthest; } } return jumps; }
时间复杂度:O(n),空间复杂度:O(1)
117. 🟡 最小栈的进阶 - 用队列实现栈(Implement Stack using Queues)- LeetCode 225
题目:使用队列实现栈的push、pop、top和empty操作。
分析:一个队列即可。push时将新元素入队后,把前面的元素依次出队再入队。
1 2 3 4 5 6 7 8 9 10 11 12 class MyStack { private Queue<Integer> queue = new LinkedList <>(); public void push (int x) { queue.offer(x); for (int i = 0 ; i < queue.size() - 1 ; i++) queue.offer(queue.poll()); } public int pop () { return queue.poll(); } public int top () { return queue.peek(); } public boolean empty () { return queue.isEmpty(); } }
时间复杂度:push O(n),其他O(1)
118. 🟡 用栈实现队列(Implement Queue using Stacks)- LeetCode 232
题目:使用栈实现队列的push、pop、peek和empty操作。
分析:两个栈。输入栈和输出栈。输出栈为空时将输入栈全部倒入输出栈。
1 2 3 4 5 6 7 8 class MyQueue { private Deque<Integer> in = new ArrayDeque <>(), out = new ArrayDeque <>(); public void push (int x) { in.push(x); } public int pop () { peek(); return out.pop(); } public int peek () { if (out.isEmpty()) while (!in.isEmpty()) out.push(in.pop()); return out.peek(); } public boolean empty () { return in.isEmpty() && out.isEmpty(); } }
时间复杂度:均摊O(1)
119. 🟡 有序矩阵中第K小的元素(Kth Smallest Element in a Sorted Matrix)- LeetCode 378
题目:n×n矩阵,每行每列升序排列,找第k小的元素。
分析:二分查找。对值域二分,统计矩阵中≤mid的元素个数。
1 2 3 4 5 6 7 8 9 10 11 12 13 public int kthSmallest (int [][] matrix, int k) { int n = matrix.length, lo = matrix[0 ][0 ], hi = matrix[n - 1 ][n - 1 ]; while (lo < hi) { int mid = lo + (hi - lo) / 2 ; int count = 0 , j = n - 1 ; for (int i = 0 ; i < n; i++) { while (j >= 0 && matrix[i][j] > mid) j--; count += j + 1 ; } if (count < k) lo = mid + 1 ; else hi = mid; } return lo; }
时间复杂度:O(n·log(max-min)),空间复杂度:O(1)
120. 🟡 打乱数组(Shuffle an Array)- LeetCode 384
题目:设计算法打乱数组(Fisher-Yates洗牌算法)。
分析:从后往前遍历,每个位置随机与前面(含自身)的某个位置交换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution { private int [] original; private Random rand = new Random (); public Solution (int [] nums) { original = nums.clone(); } public int [] reset() { return original.clone(); } public int [] shuffle() { int [] arr = original.clone(); for (int i = arr.length - 1 ; i > 0 ; i--) { int j = rand.nextInt(i + 1 ); int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } return arr; } }
时间复杂度:O(n),空间复杂度:O(n)
121. 🟡 至少有K个重复字符的最长子串(Longest Substring with At Least K Repeating)- LeetCode 395
题目:找到字符串中每个字符出现次数都不少于k的最长子串。
分析:分治法。统计字符频率,以频率<k的字符为分割点递归处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public int longestSubstring (String s, int k) { if (s.length() < k) return 0 ; int [] freq = new int [26 ]; for (char c : s.toCharArray()) freq[c - 'a' ]++; for (int i = 0 ; i < s.length(); i++) { if (freq[s.charAt(i) - 'a' ] < k) { int max = 0 ; for (String part : s.split(String.valueOf(s.charAt(i)))) max = Math.max(max, longestSubstring(part, k)); return max; } } return s.length(); }
时间复杂度:O(26·n)=O(n),空间复杂度:O(n)
122. 🟡 找出数组中重复的数字(Find All Duplicates)- LeetCode 442
题目:n个整数在[1,n]范围内,某些元素出现两次,找出所有重复的数。
分析:原地标记。将nums[abs(nums[i])-1]取负,如果已经是负数说明重复。
1 2 3 4 5 6 7 8 9 public List<Integer> findDuplicates (int [] nums) { List<Integer> res = new ArrayList <>(); for (int n : nums) { int idx = Math.abs(n) - 1 ; if (nums[idx] < 0 ) res.add(idx + 1 ); else nums[idx] = -nums[idx]; } return res; }
时间复杂度:O(n),空间复杂度:O(1)
123. 🟡 无重叠区间(Non-overlapping Intervals)- LeetCode 435
题目:给定区间集合,找到需要移除的最小区间数量,使剩余区间互不重叠。
分析:贪心。按结束时间排序,尽量保留结束早的区间。
1 2 3 4 5 6 7 8 9 public int eraseOverlapIntervals (int [][] intervals) { Arrays.sort(intervals, (a, b) -> a[1 ] - b[1 ]); int count = 0 , end = Integer.MIN_VALUE; for (int [] i : intervals) { if (i[0 ] >= end) end = i[1 ]; else count++; } return count; }
时间复杂度:O(n·log n),空间复杂度:O(1)
124. 🟡 用最少数量的箭引爆气球(Minimum Number of Arrows)- LeetCode 452
题目:二维平面上有气球(区间表示),一支箭可以引爆所有经过的气球,求最少箭数。
分析:贪心。按结束位置排序,尽量让一支箭穿过更多气球。
1 2 3 4 5 6 7 8 9 public int findMinArrowShots (int [][] points) { Arrays.sort(points, (a, b) -> Integer.compare(a[1 ], b[1 ])); int arrows = 1 ; long end = points[0 ][1 ]; for (int i = 1 ; i < points.length; i++) { if (points[i][0 ] > end) { arrows++; end = points[i][1 ]; } } return arrows; }
时间复杂度:O(n·log n),空间复杂度:O(1)
125. 🟡 N叉树的层序遍历(N-ary Tree Level Order Traversal)- LeetCode 429
题目:给定N叉树,返回其层序遍历结果。
分析:BFS,与二叉树层序遍历类似,遍历所有children。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public List<List<Integer>> levelOrder (Node root) { List<List<Integer>> res = new ArrayList <>(); if (root == null ) return res; Queue<Node> queue = new LinkedList <>(); queue.offer(root); while (!queue.isEmpty()) { int size = queue.size(); List<Integer> level = new ArrayList <>(); for (int i = 0 ; i < size; i++) { Node node = queue.poll(); level.add(node.val); for (Node child : node.children) queue.offer(child); } res.add(level); } return res; }
时间复杂度:O(n),空间复杂度:O(n)
126. 🟡 最长递增路径(Longest Increasing Path in a Matrix)- LeetCode 329
题目:给定m×n矩阵,找出最长递增路径的长度。
分析:DFS+记忆化。每个格子的最长路径只需计算一次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private int [][] dirs = {{1 ,0 },{-1 ,0 },{0 ,1 },{0 ,-1 }};public int longestIncreasingPath (int [][] matrix) { int m = matrix.length, n = matrix[0 ].length, max = 0 ; int [][] memo = new int [m][n]; for (int i = 0 ; i < m; i++) for (int j = 0 ; j < n; j++) max = Math.max(max, dfs(matrix, memo, i, j)); return max; } private int dfs (int [][] matrix, int [][] memo, int i, int j) { if (memo[i][j] != 0 ) return memo[i][j]; int max = 1 ; for (int [] d : dirs) { int ni = i + d[0 ], nj = j + d[1 ]; if (ni >= 0 && ni < matrix.length && nj >= 0 && nj < matrix[0 ].length && matrix[ni][nj] > matrix[i][j]) max = Math.max(max, 1 + dfs(matrix, memo, ni, nj)); } return memo[i][j] = max; }
时间复杂度:O(m·n),空间复杂度:O(m·n)
127. 🟡 最佳买卖股票时机含冷冻期(Best Time with Cooldown)- LeetCode 309
题目:可以多次买卖股票,但卖出后有一天冷冻期。
分析:状态机DP。三个状态:持有、不持有(冷冻期)、不持有(非冷冻期)。
1 2 3 4 5 6 7 8 9 10 public int maxProfit (int [] prices) { int hold = Integer.MIN_VALUE, sold = 0 , rest = 0 ; for (int p : prices) { int prevSold = sold; sold = hold + p; hold = Math.max(hold, rest - p); rest = Math.max(rest, prevSold); } return Math.max(sold, rest); }
时间复杂度:O(n),空间复杂度:O(1)
128. 🟡 最佳买卖股票时机含手续费(Best Time with Transaction Fee)- LeetCode 714
题目:可以多次买卖股票,每次交易需要手续费。
分析:两个状态:持有和不持有。
1 2 3 4 5 6 7 8 public int maxProfit (int [] prices, int fee) { int hold = -prices[0 ], cash = 0 ; for (int i = 1 ; i < prices.length; i++) { cash = Math.max(cash, hold + prices[i] - fee); hold = Math.max(hold, cash - prices[i]); } return cash; }
时间复杂度:O(n),空间复杂度:O(1)
129. 🟡 有效的数独(Valid Sudoku)- LeetCode 36
题目:判断9×9数独是否有效(已填入的数字是否合法)。
分析:用三个数组分别记录行、列、3×3宫格中数字是否出现过。
1 2 3 4 5 6 7 8 9 10 11 public boolean isValidSudoku (char [][] board) { boolean [][] rows = new boolean [9 ][9 ], cols = new boolean [9 ][9 ], boxes = new boolean [9 ][9 ]; for (int i = 0 ; i < 9 ; i++) for (int j = 0 ; j < 9 ; j++) { if (board[i][j] == '.' ) continue ; int num = board[i][j] - '1' , box = (i / 3 ) * 3 + j / 3 ; if (rows[i][num] || cols[j][num] || boxes[box][num]) return false ; rows[i][num] = cols[j][num] = boxes[box][num] = true ; } return true ; }
时间复杂度:O(1)(固定81格),空间复杂度:O(1)
130. 🔴 解数独(Sudoku Solver)- LeetCode 37
题目:填充数独使每行、每列、每个3×3宫格都包含1-9。
分析:回溯法。逐格尝试1-9,用行/列/宫格的布尔数组快速判断合法性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public void solveSudoku (char [][] board) { boolean [][] rows = new boolean [9 ][9 ], cols = new boolean [9 ][9 ], boxes = new boolean [9 ][9 ]; for (int i = 0 ; i < 9 ; i++) for (int j = 0 ; j < 9 ; j++) if (board[i][j] != '.' ) { int num = board[i][j] - '1' , box = (i / 3 ) * 3 + j / 3 ; rows[i][num] = cols[j][num] = boxes[box][num] = true ; } solve(board, rows, cols, boxes, 0 ); } private boolean solve (char [][] board, boolean [][] rows, boolean [][] cols, boolean [][] boxes, int pos) { if (pos == 81 ) return true ; int i = pos / 9 , j = pos % 9 ; if (board[i][j] != '.' ) return solve(board, rows, cols, boxes, pos + 1 ); int box = (i / 3 ) * 3 + j / 3 ; for (int num = 0 ; num < 9 ; num++) { if (rows[i][num] || cols[j][num] || boxes[box][num]) continue ; board[i][j] = (char ) ('1' + num); rows[i][num] = cols[j][num] = boxes[box][num] = true ; if (solve(board, rows, cols, boxes, pos + 1 )) return true ; board[i][j] = '.' ; rows[i][num] = cols[j][num] = boxes[box][num] = false ; } return false ; }
时间复杂度:O(9^空格数),空间复杂度:O(1)
131. 🟡 N皇后(N-Queens)- LeetCode 51
题目:在n×n棋盘上放置n个皇后,使它们互不攻击。
分析:回溯法。逐行放置,用三个集合记录列、主对角线、副对角线是否被占用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public List<List<String>> solveNQueens (int n) { List<List<String>> res = new ArrayList <>(); char [][] board = new char [n][n]; for (char [] row : board) Arrays.fill(row, '.' ); backtrack(res, board, 0 , new boolean [n], new boolean [2 * n], new boolean [2 * n]); return res; } private void backtrack (List<List<String>> res, char [][] board, int row, boolean [] cols, boolean [] diag1, boolean [] diag2) { int n = board.length; if (row == n) { List<String> list = new ArrayList <>(); for (char [] r : board) list.add(new String (r)); res.add(list); return ; } for (int col = 0 ; col < n; col++) { if (cols[col] || diag1[row - col + n] || diag2[row + col]) continue ; board[row][col] = 'Q' ; cols[col] = diag1[row - col + n] = diag2[row + col] = true ; backtrack(res, board, row + 1 , cols, diag1, diag2); board[row][col] = '.' ; cols[col] = diag1[row - col + n] = diag2[row + col] = false ; } }
时间复杂度:O(n!),空间复杂度:O(n²)
132. 🟡 被围绕的区域(Surrounded Regions)- LeetCode 130
题目:将被’X’围绕的’O’翻转为’X’,边界上的’O’及其连通的’O’不翻转。
分析:从边界的’O’开始DFS标记为安全,然后将未标记的’O’翻转。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public void solve (char [][] board) { int m = board.length, n = board[0 ].length; for (int i = 0 ; i < m; i++) { dfs(board, i, 0 ); dfs(board, i, n - 1 ); } for (int j = 0 ; j < n; j++) { dfs(board, 0 , j); dfs(board, m - 1 , j); } for (int i = 0 ; i < m; i++) for (int j = 0 ; j < n; j++) { if (board[i][j] == 'O' ) board[i][j] = 'X' ; else if (board[i][j] == '#' ) board[i][j] = 'O' ; } } private void dfs (char [][] board, int i, int j) { if (i < 0 || i >= board.length || j < 0 || j >= board[0 ].length || board[i][j] != 'O' ) return ; board[i][j] = '#' ; dfs(board, i + 1 , j); dfs(board, i - 1 , j); dfs(board, i, j + 1 ); dfs(board, i, j - 1 ); }
时间复杂度:O(m·n),空间复杂度:O(m·n)
133. 🟡 克隆图(Clone Graph)- LeetCode 133
题目:给定无向连通图中一个节点的引用,返回该图的深拷贝。
分析:DFS/BFS + HashMap记录已克隆的节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 public Node cloneGraph (Node node) { if (node == null ) return null ; Map<Node, Node> map = new HashMap <>(); return dfs(node, map); } private Node dfs (Node node, Map<Node, Node> map) { if (map.containsKey(node)) return map.get(node); Node clone = new Node (node.val); map.put(node, clone); for (Node neighbor : node.neighbors) clone.neighbors.add(dfs(neighbor, map)); return clone; }
时间复杂度:O(V+E),空间复杂度:O(V)
134. 🟡 单词接龙(Word Ladder)- LeetCode 127
题目:给定起始单词和结束单词以及字典,每次只能改变一个字母,求最短转换序列长度。
分析:BFS。每个单词是图中的节点,相差一个字母的单词之间有边。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public int ladderLength (String beginWord, String endWord, List<String> wordList) { Set<String> dict = new HashSet <>(wordList); if (!dict.contains(endWord)) return 0 ; Queue<String> queue = new LinkedList <>(); queue.offer(beginWord); int level = 1 ; while (!queue.isEmpty()) { int size = queue.size(); level++; for (int i = 0 ; i < size; i++) { char [] word = queue.poll().toCharArray(); for (int j = 0 ; j < word.length; j++) { char orig = word[j]; for (char c = 'a' ; c <= 'z' ; c++) { if (c == orig) continue ; word[j] = c; String next = new String (word); if (next.equals(endWord)) return level; if (dict.remove(next)) queue.offer(next); } word[j] = orig; } } } return 0 ; }
时间复杂度:O(n·m·26),n为字典大小,m为单词长度
135. 🟡 最小路径和变体 - 三角形最小路径和(Triangle)- LeetCode 120
题目:给定三角形,找出从顶到底的最小路径和。
分析:自底向上DP。dp[j] = min(dp[j], dp[j+1]) + triangle[i][j]。
1 2 3 4 5 6 7 8 public int minimumTotal (List<List<Integer>> triangle) { int n = triangle.size(); int [] dp = new int [n + 1 ]; for (int i = n - 1 ; i >= 0 ; i--) for (int j = 0 ; j <= i; j++) dp[j] = Math.min(dp[j], dp[j + 1 ]) + triangle.get(i).get(j); return dp[0 ]; }
时间复杂度:O(n²),空间复杂度:O(n)
136. 🟡 分割回文串(Palindrome Partitioning)- LeetCode 131
题目:将字符串分割成若干子串,使每个子串都是回文串,返回所有可能的分割方案。
分析:回溯+预处理回文判断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public List<List<String>> partition (String s) { int n = s.length(); boolean [][] isPalin = new boolean [n][n]; for (int i = n - 1 ; i >= 0 ; i--) for (int j = i; j < n; j++) isPalin[i][j] = s.charAt(i) == s.charAt(j) && (j - i <= 2 || isPalin[i + 1 ][j - 1 ]); List<List<String>> res = new ArrayList <>(); backtrack(res, new ArrayList <>(), s, isPalin, 0 ); return res; } private void backtrack (List<List<String>> res, List<String> path, String s, boolean [][] isPalin, int start) { if (start == s.length()) { res.add(new ArrayList <>(path)); return ; } for (int end = start; end < s.length(); end++) { if (isPalin[start][end]) { path.add(s.substring(start, end + 1 )); backtrack(res, path, s, isPalin, end + 1 ); path.remove(path.size() - 1 ); } } }
时间复杂度:O(n·2^n),空间复杂度:O(n²)
137. 🟡 复制带随机指针的链表(Copy List with Random Pointer)- LeetCode 138
题目:深拷贝带有random指针的链表。
分析:三步法:1.在每个节点后插入拷贝节点 2.设置拷贝节点的random 3.拆分链表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public Node copyRandomList (Node head) { if (head == null ) return null ; for (Node curr = head; curr != null ; ) { Node copy = new Node (curr.val); copy.next = curr.next; curr.next = copy; curr = copy.next; } for (Node curr = head; curr != null ; curr = curr.next.next) if (curr.random != null ) curr.next.random = curr.random.next; Node newHead = head.next; for (Node curr = head; curr != null ; ) { Node copy = curr.next; curr.next = copy.next; copy.next = (curr.next != null ) ? curr.next.next : null ; curr = curr.next; } return newHead; }
时间复杂度:O(n),空间复杂度:O(1)
138. 🟡 两两交换链表中的节点(Swap Nodes in Pairs)- LeetCode 24
题目:两两交换链表中相邻的节点。
分析:迭代法,用dummy节点简化边界处理。
1 2 3 4 5 6 7 8 9 public ListNode swapPairs (ListNode head) { ListNode dummy = new ListNode (0 , head), prev = dummy; while (prev.next != null && prev.next.next != null ) { ListNode a = prev.next, b = a.next; a.next = b.next; b.next = a; prev.next = b; prev = a; } return dummy.next; }
时间复杂度:O(n),空间复杂度:O(1)
139. 🟡 整数转罗马数字(Integer to Roman)- LeetCode 12
题目:将整数转换为罗马数字。
分析:贪心。从大到小匹配所有可能的罗马数字组合。
1 2 3 4 5 6 7 8 public String intToRoman (int num) { int [] vals = {1000 ,900 ,500 ,400 ,100 ,90 ,50 ,40 ,10 ,9 ,5 ,4 ,1 }; String[] syms = {"M" ,"CM" ,"D" ,"CD" ,"C" ,"XC" ,"L" ,"XL" ,"X" ,"IX" ,"V" ,"IV" ,"I" }; StringBuilder sb = new StringBuilder (); for (int i = 0 ; i < vals.length; i++) while (num >= vals[i]) { sb.append(syms[i]); num -= vals[i]; } return sb.toString(); }
时间复杂度:O(1),空间复杂度:O(1)
140. 🟡 字符串转换整数(String to Integer / atoi)- LeetCode 8
题目:实现atoi函数,将字符串转换为整数。
分析:处理前导空格、正负号、数字字符、溢出。状态机或直接模拟。
1 2 3 4 5 6 7 8 9 10 11 12 public int myAtoi (String s) { int i = 0 , n = s.length(), sign = 1 ; long result = 0 ; while (i < n && s.charAt(i) == ' ' ) i++; if (i < n && (s.charAt(i) == '+' || s.charAt(i) == '-' )) sign = s.charAt(i++) == '-' ? -1 : 1 ; while (i < n && Character.isDigit(s.charAt(i))) { result = result * 10 + (s.charAt(i++) - '0' ); if (result * sign > Integer.MAX_VALUE) return Integer.MAX_VALUE; if (result * sign < Integer.MIN_VALUE) return Integer.MIN_VALUE; } return (int ) (result * sign); }
时间复杂度:O(n),空间复杂度:O(1)
141. 🟡 Z字形变换(Zigzag Conversion)- LeetCode 6
题目:将字符串按Z字形排列后逐行读取。
分析:模拟。用numRows个StringBuilder,按方向交替填充。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public String convert (String s, int numRows) { if (numRows == 1 ) return s; StringBuilder[] rows = new StringBuilder [numRows]; for (int i = 0 ; i < numRows; i++) rows[i] = new StringBuilder (); int row = 0 , dir = -1 ; for (char c : s.toCharArray()) { rows[row].append(c); if (row == 0 || row == numRows - 1 ) dir = -dir; row += dir; } StringBuilder res = new StringBuilder (); for (StringBuilder r : rows) res.append(r); return res.toString(); }
时间复杂度:O(n),空间复杂度:O(n)
142. 🟡 四数之和(4Sum)- LeetCode 18
题目:找出数组中所有和为target的四元组。
分析:排序+双层循环+双指针。外两层固定两个数,内层双指针找另外两个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public List<List<Integer>> fourSum (int [] nums, int target) { List<List<Integer>> res = new ArrayList <>(); Arrays.sort(nums); for (int i = 0 ; i < nums.length - 3 ; i++) { if (i > 0 && nums[i] == nums[i - 1 ]) continue ; for (int j = i + 1 ; j < nums.length - 2 ; j++) { if (j > i + 1 && nums[j] == nums[j - 1 ]) continue ; int left = j + 1 , right = nums.length - 1 ; while (left < right) { long sum = (long ) nums[i] + nums[j] + nums[left] + nums[right]; if (sum == target) { res.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right])); while (left < right && nums[left] == nums[left + 1 ]) left++; while (left < right && nums[right] == nums[right - 1 ]) right--; left++; right--; } else if (sum < target) left++; else right--; } } } return res; }
时间复杂度:O(n³),空间复杂度:O(1)
143. 🟡 最接近的三数之和(3Sum Closest)- LeetCode 16
题目:找出数组中三个数的和最接近target的值。
分析:排序+双指针。与三数之和类似,维护最接近的差值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public int threeSumClosest (int [] nums, int target) { Arrays.sort(nums); int closest = nums[0 ] + nums[1 ] + nums[2 ]; for (int i = 0 ; i < nums.length - 2 ; i++) { int left = i + 1 , right = nums.length - 1 ; while (left < right) { int sum = nums[i] + nums[left] + nums[right]; if (Math.abs(sum - target) < Math.abs(closest - target)) closest = sum; if (sum < target) left++; else if (sum > target) right--; else return target; } } return closest; }
时间复杂度:O(n²),空间复杂度:O(1)
144. 🟡 删除排序链表中的重复元素 II(Remove Duplicates II)- LeetCode 82
题目:删除排序链表中所有重复的元素,只保留原始链表中没有重复出现的元素。
分析:用dummy节点,prev指向上一个确认不重复的节点。
1 2 3 4 5 6 7 8 9 10 11 public ListNode deleteDuplicates (ListNode head) { ListNode dummy = new ListNode (0 , head), prev = dummy; while (prev.next != null ) { ListNode curr = prev.next; if (curr.next != null && curr.val == curr.next.val) { while (curr.next != null && curr.val == curr.next.val) curr = curr.next; prev.next = curr.next; } else prev = prev.next; } return dummy.next; }
时间复杂度:O(n),空间复杂度:O(1)
145. 🟡 反转链表 II(Reverse Linked List II)- LeetCode 92
题目:反转链表从位置left到right的部分。
分析:找到left前一个节点,然后用头插法逐个反转。
1 2 3 4 5 6 7 8 9 10 11 12 public ListNode reverseBetween (ListNode head, int left, int right) { ListNode dummy = new ListNode (0 , head), prev = dummy; for (int i = 1 ; i < left; i++) prev = prev.next; ListNode curr = prev.next; for (int i = 0 ; i < right - left; i++) { ListNode next = curr.next; curr.next = next.next; next.next = prev.next; prev.next = next; } return dummy.next; }
时间复杂度:O(n),空间复杂度:O(1)
146. 🟡 子集 II(Subsets II)- LeetCode 90
题目:给定可能包含重复元素的整数数组,返回所有可能的子集(不含重复子集)。
分析:排序+回溯,同层跳过重复元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public List<List<Integer>> subsetsWithDup (int [] nums) { List<List<Integer>> res = new ArrayList <>(); Arrays.sort(nums); backtrack(res, new ArrayList <>(), nums, 0 ); return res; } private void backtrack (List<List<Integer>> res, List<Integer> path, int [] nums, int start) { res.add(new ArrayList <>(path)); for (int i = start; i < nums.length; i++) { if (i > start && nums[i] == nums[i - 1 ]) continue ; path.add(nums[i]); backtrack(res, path, nums, i + 1 ); path.remove(path.size() - 1 ); } }
时间复杂度:O(n·2^n),空间复杂度:O(n)
147. 🟡 组合总和 II(Combination Sum II)- LeetCode 40
题目:给定有重复元素的数组和目标值,找出所有和为目标值的组合,每个数字只能用一次。
分析:排序+回溯+同层去重。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public List<List<Integer>> combinationSum2 (int [] candidates, int target) { List<List<Integer>> res = new ArrayList <>(); Arrays.sort(candidates); backtrack(res, new ArrayList <>(), candidates, target, 0 ); return res; } private void backtrack (List<List<Integer>> res, List<Integer> path, int [] nums, int remain, int start) { if (remain == 0 ) { res.add(new ArrayList <>(path)); return ; } for (int i = start; i < nums.length && nums[i] <= remain; i++) { if (i > start && nums[i] == nums[i - 1 ]) continue ; path.add(nums[i]); backtrack(res, path, nums, remain - nums[i], i + 1 ); path.remove(path.size() - 1 ); } }
时间复杂度:O(2^n),空间复杂度:O(n)
148. 🟡 全排列 II(Permutations II)- LeetCode 47
题目:给定可包含重复数字的序列,返回所有不重复的全排列。
分析:排序+回溯+剪枝。同层中相同的数字只选第一个(前一个相同数字未使用时跳过)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public List<List<Integer>> permuteUnique (int [] nums) { List<List<Integer>> res = new ArrayList <>(); Arrays.sort(nums); backtrack(res, new ArrayList <>(), nums, new boolean [nums.length]); return res; } private void backtrack (List<List<Integer>> res, List<Integer> path, int [] nums, boolean [] used) { if (path.size() == nums.length) { res.add(new ArrayList <>(path)); return ; } for (int i = 0 ; i < nums.length; i++) { if (used[i] || (i > 0 && nums[i] == nums[i - 1 ] && !used[i - 1 ])) continue ; used[i] = true ; path.add(nums[i]); backtrack(res, path, nums, used); path.remove(path.size() - 1 ); used[i] = false ; } }
时间复杂度:O(n·n!),空间复杂度:O(n)
149. 🟡 Pow(x, n)(快速幂)- LeetCode 50
题目:实现pow(x, n)。
分析:快速幂。n为偶数时x^n = (x²)^(n/2),n为奇数时多乘一个x。注意n为负数和Integer.MIN_VALUE。
1 2 3 4 5 6 7 8 9 10 11 public double myPow (double x, int n) { long N = n; if (N < 0 ) { x = 1 / x; N = -N; } double result = 1 ; while (N > 0 ) { if ((N & 1 ) == 1 ) result *= x; x *= x; N >>= 1 ; } return result; }
时间复杂度:O(log n),空间复杂度:O(1)
150. 🟡 寻找峰值(Find Peak Element)- LeetCode 162
题目:数组中相邻元素不相等,找到一个峰值元素(大于左右邻居),要求O(log n)。
分析:二分查找。如果mid < mid+1,峰值在右半边;否则在左半边。
1 2 3 4 5 6 7 8 9 public int findPeakElement (int [] nums) { int lo = 0 , hi = nums.length - 1 ; while (lo < hi) { int mid = lo + (hi - lo) / 2 ; if (nums[mid] < nums[mid + 1 ]) lo = mid + 1 ; else hi = mid; } return lo; }
时间复杂度:O(log n),空间复杂度:O(1)
151. 🟡 两数相除(Divide Two Integers)- LeetCode 29
题目:不使用乘法、除法和取余运算符,实现两个整数的除法。
分析:用位运算模拟。每次将除数左移(翻倍),找到最大的倍数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public int divide (int dividend, int divisor) { if (dividend == Integer.MIN_VALUE && divisor == -1 ) return Integer.MAX_VALUE; int sign = (dividend > 0 ) ^ (divisor > 0 ) ? -1 : 1 ; long a = Math.abs((long ) dividend), b = Math.abs((long ) divisor); int result = 0 ; while (a >= b) { long temp = b; int multiple = 1 ; while (a >= (temp << 1 )) { temp <<= 1 ; multiple <<= 1 ; } a -= temp; result += multiple; } return sign * result; }
时间复杂度:O(log²n),空间复杂度:O(1)
152. 🟡 排列序列(Permutation Sequence)- LeetCode 60
题目:给定n和k,返回第k个排列。
分析:数学法。利用阶乘确定每一位的数字,避免生成所有排列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public String getPermutation (int n, int k) { int [] factorial = new int [n + 1 ]; factorial[0 ] = 1 ; for (int i = 1 ; i <= n; i++) factorial[i] = factorial[i - 1 ] * i; List<Integer> nums = new ArrayList <>(); for (int i = 1 ; i <= n; i++) nums.add(i); StringBuilder sb = new StringBuilder (); k--; for (int i = n - 1 ; i >= 0 ; i--) { int idx = k / factorial[i]; sb.append(nums.remove(idx)); k %= factorial[i]; } return sb.toString(); }
时间复杂度:O(n²),空间复杂度:O(n)
153. 🟡 搜索旋转排序数组 II(Search in Rotated Sorted Array II)- LeetCode 81
题目:旋转排序数组中可能有重复元素,搜索目标值。
分析:与33题类似,但有重复时无法判断哪半边有序,需要缩小边界。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public boolean search (int [] nums, int target) { int lo = 0 , hi = nums.length - 1 ; while (lo <= hi) { int mid = lo + (hi - lo) / 2 ; if (nums[mid] == target) return true ; if (nums[lo] == nums[mid]) { lo++; continue ; } if (nums[lo] <= nums[mid]) { if (target >= nums[lo] && target < nums[mid]) hi = mid - 1 ; else lo = mid + 1 ; } else { if (target > nums[mid] && target <= nums[hi]) lo = mid + 1 ; else hi = mid - 1 ; } } return false ; }
时间复杂度:平均O(log n),最坏O(n),空间复杂度:O(1)
154. 🟡 寻找旋转排序数组中的最小值(Find Minimum in Rotated Sorted Array)- LeetCode 153
题目:在旋转排序数组中找到最小值。
分析:二分查找。比较mid和right,mid>right说明最小值在右半边。
1 2 3 4 5 6 7 8 9 public int findMin (int [] nums) { int lo = 0 , hi = nums.length - 1 ; while (lo < hi) { int mid = lo + (hi - lo) / 2 ; if (nums[mid] > nums[hi]) lo = mid + 1 ; else hi = mid; } return nums[lo]; }
时间复杂度:O(log n),空间复杂度:O(1)
155. 🟡 最大数(Largest Number)- LeetCode 179
题目:给定一组非负整数,重新排列使之组成最大的整数。
分析:自定义排序。比较a+b和b+a的字典序。
1 2 3 4 5 6 7 8 9 public String largestNumber (int [] nums) { String[] strs = new String [nums.length]; for (int i = 0 ; i < nums.length; i++) strs[i] = String.valueOf(nums[i]); Arrays.sort(strs, (a, b) -> (b + a).compareTo(a + b)); if (strs[0 ].equals("0" )) return "0" ; StringBuilder sb = new StringBuilder (); for (String s : strs) sb.append(s); return sb.toString(); }
时间复杂度:O(n·log n·k),k为数字平均位数,空间复杂度:O(n)
156. 🟡 位1的个数(Number of 1 Bits)- LeetCode 191
题目:返回无符号整数的二进制中1的个数。
分析:n & (n-1)消除最低位的1,计数直到n为0。
1 2 3 4 5 public int hammingWeight (int n) { int count = 0 ; while (n != 0 ) { n &= (n - 1 ); count++; } return count; }
时间复杂度:O(k),k为1的个数,空间复杂度:O(1)
157. 🟡 旋转数组(Rotate Array)- LeetCode 189
题目:将数组向右旋转k个位置。
分析:三次翻转。先整体翻转,再翻转前k个,再翻转后n-k个。
1 2 3 4 5 6 7 8 9 10 public void rotate (int [] nums, int k) { k %= nums.length; reverse(nums, 0 , nums.length - 1 ); reverse(nums, 0 , k - 1 ); reverse(nums, k, nums.length - 1 ); } private void reverse (int [] nums, int l, int r) { while (l < r) { int t = nums[l]; nums[l++] = nums[r]; nums[r--] = t; } }
时间复杂度:O(n),空间复杂度:O(1)
158. 🟡 只出现一次的数字(Single Number)- LeetCode 136
题目:数组中除了一个元素只出现一次外,其余都出现两次,找出那个只出现一次的。
分析:异或。a^a=0,0^a=a,所有数异或的结果就是只出现一次的数。
1 2 3 4 5 public int singleNumber (int [] nums) { int result = 0 ; for (int n : nums) result ^= n; return result; }
时间复杂度:O(n),空间复杂度:O(1)
159. 🟡 只出现一次的数字 III(Single Number III)- LeetCode 260
题目:数组中恰好有两个元素只出现一次,其余都出现两次,找出这两个元素。
分析:全部异或得到两个数的异或值,找到任意一个为1的位,按该位分组分别异或。
1 2 3 4 5 6 7 8 9 10 public int [] singleNumber(int [] nums) { int xor = 0 ; for (int n : nums) xor ^= n; int bit = xor & (-xor); int a = 0 , b = 0 ; for (int n : nums) { if ((n & bit) == 0 ) a ^= n; else b ^= n; } return new int []{a, b}; }
时间复杂度:O(n),空间复杂度:O(1)
160. 🟡 二叉搜索树中第K小的元素(Kth Smallest Element in a BST)- LeetCode 230
题目:给定BST的根节点和整数k,返回第k小的元素。
分析:中序遍历BST得到升序序列,第k个就是答案。
1 2 3 4 5 6 7 8 9 10 11 public int kthSmallest (TreeNode root, int k) { Deque<TreeNode> stack = new ArrayDeque <>(); TreeNode curr = root; while (curr != null || !stack.isEmpty()) { while (curr != null ) { stack.push(curr); curr = curr.left; } curr = stack.pop(); if (--k == 0 ) return curr.val; curr = curr.right; } return -1 ; }
时间复杂度:O(H+k),空间复杂度:O(H)
161. 🟡 二叉搜索树迭代器(BST Iterator)- LeetCode 173
题目:实现BST的迭代器,支持next()和hasNext(),均为O(1)均摊时间。
分析:用栈模拟中序遍历,先将所有左节点入栈。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class BSTIterator { private Deque<TreeNode> stack = new ArrayDeque <>(); public BSTIterator (TreeNode root) { pushLeft(root); } public int next () { TreeNode node = stack.pop(); pushLeft(node.right); return node.val; } public boolean hasNext () { return !stack.isEmpty(); } private void pushLeft (TreeNode node) { while (node != null ) { stack.push(node); node = node.left; } } }
时间复杂度:next()均摊O(1),空间复杂度:O(h)
162. 🟡 最小基因变化(Minimum Genetic Mutation)- LeetCode 433
题目:基因序列由ACGT组成,每次变化一个字符,求从start到end的最少变化次数。
分析:BFS,与单词接龙类似。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public int minMutation (String startGene, String endGene, String[] bank) { Set<String> dict = new HashSet <>(Arrays.asList(bank)); if (!dict.contains(endGene)) return -1 ; Queue<String> queue = new LinkedList <>(); queue.offer(startGene); char [] genes = {'A' , 'C' , 'G' , 'T' }; int steps = 0 ; while (!queue.isEmpty()) { int size = queue.size(); steps++; for (int i = 0 ; i < size; i++) { char [] curr = queue.poll().toCharArray(); for (int j = 0 ; j < 8 ; j++) { char orig = curr[j]; for (char g : genes) { if (g == orig) continue ; curr[j] = g; String next = new String (curr); if (next.equals(endGene)) return steps; if (dict.remove(next)) queue.offer(next); } curr[j] = orig; } } } return -1 ; }
时间复杂度:O(n·8·4),空间复杂度:O(n)
163. 🟡 打家劫舍 II(House Robber II)- LeetCode 213
题目:房屋排成环形,不能偷相邻的,求最大金额。
分析:环形问题拆成两个线性问题:偷[0,n-2]和偷[1,n-1],取较大值。
1 2 3 4 5 6 7 8 9 10 public int rob (int [] nums) { if (nums.length == 1 ) return nums[0 ]; return Math.max(robRange(nums, 0 , nums.length - 2 ), robRange(nums, 1 , nums.length - 1 )); } private int robRange (int [] nums, int start, int end) { int prev = 0 , curr = 0 ; for (int i = start; i <= end; i++) { int tmp = curr; curr = Math.max(curr, prev + nums[i]); prev = tmp; } return curr; }
时间复杂度:O(n),空间复杂度:O(1)
164. 🟡 丑数 II(Ugly Number II)- LeetCode 264
题目:找出第n个丑数(只包含质因子2、3、5的正整数)。
分析:三指针法。维护三个指针分别乘以2、3、5,每次取最小值。
1 2 3 4 5 6 7 8 9 10 11 12 13 public int nthUglyNumber (int n) { int [] dp = new int [n]; dp[0 ] = 1 ; int p2 = 0 , p3 = 0 , p5 = 0 ; for (int i = 1 ; i < n; i++) { int v2 = dp[p2] * 2 , v3 = dp[p3] * 3 , v5 = dp[p5] * 5 ; dp[i] = Math.min(v2, Math.min(v3, v5)); if (dp[i] == v2) p2++; if (dp[i] == v3) p3++; if (dp[i] == v5) p5++; } return dp[n - 1 ]; }
时间复杂度:O(n),空间复杂度:O(n)
165. 🟡 两个整数相加(Sum of Two Integers)- LeetCode 371
题目:不使用+和-运算符,计算两个整数之和。
分析:位运算。异或得到不进位的和,与运算左移一位得到进位,循环直到进位为0。
1 2 3 4 5 6 7 8 public int getSum (int a, int b) { while (b != 0 ) { int carry = (a & b) << 1 ; a = a ^ b; b = carry; } return a; }
时间复杂度:O(1),空间复杂度:O(1)
166. 🟡 重排链表(Reorder List)- LeetCode 143
题目:将链表L0→L1→…→Ln重排为L0→Ln→L1→Ln-1→…
分析:三步:1.快慢指针找中点 2.反转后半部分 3.交替合并。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public void reorderList (ListNode head) { ListNode slow = head, fast = head; while (fast.next != null && fast.next.next != null ) { slow = slow.next; fast = fast.next.next; } ListNode second = reverse(slow.next); slow.next = null ; ListNode first = head; while (second != null ) { ListNode tmp1 = first.next, tmp2 = second.next; first.next = second; second.next = tmp1; first = tmp1; second = tmp2; } } private ListNode reverse (ListNode head) { ListNode prev = null ; while (head != null ) { ListNode next = head.next; head.next = prev; prev = head; head = next; } return prev; }
时间复杂度:O(n),空间复杂度:O(1)
167. 🟡 奇偶链表(Odd Even Linked List)- LeetCode 328
题目:将链表中奇数位置节点排在前面,偶数位置节点排在后面。
分析:双指针分别串联奇数位和偶数位节点,最后连接。
1 2 3 4 5 6 7 8 9 10 public ListNode oddEvenList (ListNode head) { if (head == null ) return null ; ListNode odd = head, even = head.next, evenHead = even; while (even != null && even.next != null ) { odd.next = even.next; odd = odd.next; even.next = odd.next; even = even.next; } odd.next = evenHead; return head; }
时间复杂度:O(n),空间复杂度:O(1)
168. 🟡 加油站(Gas Station)- LeetCode 134
题目:环形路线上有n个加油站,判断能否绕一圈,返回起始加油站编号。
分析:贪心。如果总油量≥总消耗,一定有解。从净油量累计最低点的下一个站出发。
1 2 3 4 5 6 7 8 9 10 public int canCompleteCircuit (int [] gas, int [] cost) { int total = 0 , curr = 0 , start = 0 ; for (int i = 0 ; i < gas.length; i++) { int diff = gas[i] - cost[i]; total += diff; curr += diff; if (curr < 0 ) { start = i + 1 ; curr = 0 ; } } return total >= 0 ? start : -1 ; }
时间复杂度:O(n),空间复杂度:O(1)
169. 🟡 分发糖果(Candy)- LeetCode 135
题目:n个孩子站成一排,每个孩子有评分,分发糖果使得评分高的孩子比相邻的孩子获得更多糖果,求最少糖果数。
分析:两次遍历。从左到右保证右边比左边高的多一个,从右到左保证左边比右边高的多一个。
1 2 3 4 5 6 7 8 9 10 11 12 public int candy (int [] ratings) { int n = ratings.length; int [] candies = new int [n]; Arrays.fill(candies, 1 ); for (int i = 1 ; i < n; i++) if (ratings[i] > ratings[i - 1 ]) candies[i] = candies[i - 1 ] + 1 ; for (int i = n - 2 ; i >= 0 ; i--) if (ratings[i] > ratings[i + 1 ]) candies[i] = Math.max(candies[i], candies[i + 1 ] + 1 ); int sum = 0 ; for (int c : candies) sum += c; return sum; }
时间复杂度:O(n),空间复杂度:O(n)
170. 🟡 基本计算器 II(Basic Calculator II)- LeetCode 227
题目:实现一个基本的计算器,支持+、-、*、/和空格。
分析:用栈处理。遇到*和/立即计算,+和-将数字入栈(-号取负),最后栈中所有数求和。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public int calculate (String s) { Deque<Integer> stack = new ArrayDeque <>(); int num = 0 ; char op = '+' ; for (int i = 0 ; i < s.length(); i++) { char c = s.charAt(i); if (Character.isDigit(c)) num = num * 10 + (c - '0' ); if ((!Character.isDigit(c) && c != ' ' ) || i == s.length() - 1 ) { if (op == '+' ) stack.push(num); else if (op == '-' ) stack.push(-num); else if (op == '*' ) stack.push(stack.pop() * num); else if (op == '/' ) stack.push(stack.pop() / num); op = c; num = 0 ; } } int result = 0 ; for (int n : stack) result += n; return result; }
时间复杂度:O(n),空间复杂度:O(n)
171. 🟡 最长连续递增序列(Longest Continuous Increasing Subsequence)- LeetCode 674
题目:给定未排序的整数数组,找出最长连续递增子序列的长度。
分析:一次遍历,维护当前连续递增长度。
1 2 3 4 5 6 7 8 public int findLengthOfLCIS (int [] nums) { int max = 1 , curr = 1 ; for (int i = 1 ; i < nums.length; i++) { curr = nums[i] > nums[i - 1 ] ? curr + 1 : 1 ; max = Math.max(max, curr); } return max; }
时间复杂度:O(n),空间复杂度:O(1)
172. 🟡 有效的字母异位词(Valid Anagram)- LeetCode 242
题目:判断两个字符串是否是字母异位词。
分析:字符计数数组比较。
1 2 3 4 5 6 7 public boolean isAnagram (String s, String t) { if (s.length() != t.length()) return false ; int [] count = new int [26 ]; for (int i = 0 ; i < s.length(); i++) { count[s.charAt(i) - 'a' ]++; count[t.charAt(i) - 'a' ]--; } for (int c : count) if (c != 0 ) return false ; return true ; }
时间复杂度:O(n),空间复杂度:O(1)
173. 🟡 最小绝对差(Minimum Absolute Difference in BST)- LeetCode 530
题目:给定BST,找出任意两个节点值之间的最小绝对差。
分析:中序遍历BST得到有序序列,最小差一定在相邻元素之间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private int minDiff = Integer.MAX_VALUE;private TreeNode prev = null ;public int getMinimumDifference (TreeNode root) { inorder(root); return minDiff; } private void inorder (TreeNode node) { if (node == null ) return ; inorder(node.left); if (prev != null ) minDiff = Math.min(minDiff, node.val - prev.val); prev = node; inorder(node.right); }
时间复杂度:O(n),空间复杂度:O(h)
174. 🟡 两数之和 II - 输入有序数组(Two Sum II)- LeetCode 167
题目:在有序数组中找到两个数使得它们的和等于目标值。
分析:双指针从两端向中间收缩。
1 2 3 4 5 6 7 8 9 10 public int [] twoSum(int [] numbers, int target) { int left = 0 , right = numbers.length - 1 ; while (left < right) { int sum = numbers[left] + numbers[right]; if (sum == target) return new int []{left + 1 , right + 1 }; else if (sum < target) left++; else right--; } return new int [0 ]; }
时间复杂度:O(n),空间复杂度:O(1)
175. 🟡 最长字符串链(Longest String Chain)- LeetCode 1048
题目:给定单词列表,找出最长的字符串链(每次添加一个字母形成新单词)。
分析:按长度排序,DP。对每个单词尝试删除每个字符,看前驱是否存在。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public int longestStrChain (String[] words) { Arrays.sort(words, (a, b) -> a.length() - b.length()); Map<String, Integer> dp = new HashMap <>(); int max = 1 ; for (String word : words) { int best = 1 ; for (int i = 0 ; i < word.length(); i++) { String prev = word.substring(0 , i) + word.substring(i + 1 ); best = Math.max(best, dp.getOrDefault(prev, 0 ) + 1 ); } dp.put(word, best); max = Math.max(max, best); } return max; }
时间复杂度:O(n·m²),m为单词最大长度,空间复杂度:O(n·m)
176. 🟡 岛屿的最大面积(Max Area of Island)- LeetCode 695
题目:给定二维网格,找出最大岛屿的面积。
分析:DFS,每次访问一个’1’就计数并标记为已访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 public int maxAreaOfIsland (int [][] grid) { int max = 0 ; for (int i = 0 ; i < grid.length; i++) for (int j = 0 ; j < grid[0 ].length; j++) if (grid[i][j] == 1 ) max = Math.max(max, dfs(grid, i, j)); return max; } private int dfs (int [][] grid, int i, int j) { if (i < 0 || i >= grid.length || j < 0 || j >= grid[0 ].length || grid[i][j] != 1 ) return 0 ; grid[i][j] = 0 ; return 1 + dfs(grid, i + 1 , j) + dfs(grid, i - 1 , j) + dfs(grid, i, j + 1 ) + dfs(grid, i, j - 1 ); }
时间复杂度:O(m·n),空间复杂度:O(m·n)
177. 🟡 字符串相乘(Multiply Strings)- LeetCode 43
题目:给定两个以字符串形式表示的非负整数,返回它们的乘积。
分析:模拟竖式乘法。num1[i] * num2[j]的结果放在result[i+j]和result[i+j+1]。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public String multiply (String num1, String num2) { int m = num1.length(), n = num2.length(); int [] result = new int [m + n]; for (int i = m - 1 ; i >= 0 ; i--) for (int j = n - 1 ; j >= 0 ; j--) { int mul = (num1.charAt(i) - '0' ) * (num2.charAt(j) - '0' ); int p1 = i + j, p2 = i + j + 1 ; int sum = mul + result[p2]; result[p2] = sum % 10 ; result[p1] += sum / 10 ; } StringBuilder sb = new StringBuilder (); for (int r : result) if (!(sb.length() == 0 && r == 0 )) sb.append(r); return sb.length() == 0 ? "0" : sb.toString(); }
时间复杂度:O(m·n),空间复杂度:O(m+n)
178. 🟡 LFU缓存(LFU Cache)- LeetCode 460
题目:设计LFU缓存,支持get和put操作,均为O(1)。
分析:HashMap + 频率到双向链表的映射。维护最小频率。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class LFUCache { private Map<Integer, int []> cache = new HashMap <>(); private Map<Integer, LinkedHashSet<Integer>> freqMap = new HashMap <>(); private int capacity, minFreq; public LFUCache (int capacity) { this .capacity = capacity; } public int get (int key) { if (!cache.containsKey(key)) return -1 ; int [] info = cache.get(key); updateFreq(key, info[1 ]); info[1 ]++; return info[0 ]; } public void put (int key, int value) { if (capacity == 0 ) return ; if (cache.containsKey(key)) { int [] info = cache.get(key); info[0 ] = value; updateFreq(key, info[1 ]); info[1 ]++; return ; } if (cache.size() == capacity) { int evict = freqMap.get(minFreq).iterator().next(); freqMap.get(minFreq).remove(evict); cache.remove(evict); } cache.put(key, new int []{value, 1 }); freqMap.computeIfAbsent(1 , k -> new LinkedHashSet <>()).add(key); minFreq = 1 ; } private void updateFreq (int key, int freq) { freqMap.get(freq).remove(key); if (freqMap.get(freq).isEmpty()) { freqMap.remove(freq); if (minFreq == freq) minFreq++; } freqMap.computeIfAbsent(freq + 1 , k -> new LinkedHashSet <>()).add(key); } }
时间复杂度:get/put均O(1),空间复杂度:O(capacity)
179. 🟡 设计哈希映射(Design HashMap)- LeetCode 706
题目:不使用内置哈希表库,设计HashMap。
分析:拉链法。数组+链表,哈希函数取模。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class MyHashMap { private static final int SIZE = 1009 ; private List<int []>[] buckets; public MyHashMap () { buckets = new List [SIZE]; for (int i = 0 ; i < SIZE; i++) buckets[i] = new ArrayList <>(); } public void put (int key, int value) { int idx = key % SIZE; for (int [] pair : buckets[idx]) { if (pair[0 ] == key) { pair[1 ] = value; return ; } } buckets[idx].add(new int []{key, value}); } public int get (int key) { for (int [] pair : buckets[key % SIZE]) if (pair[0 ] == key) return pair[1 ]; return -1 ; } public void remove (int key) { buckets[key % SIZE].removeIf(p -> p[0 ] == key); } }
时间复杂度:均摊O(1),空间复杂度:O(n)
180. 🟡 最长有效括号变体 - 删除无效的括号(Remove Invalid Parentheses)- LeetCode 301
题目:删除最少数量的无效括号,使字符串有效,返回所有可能的结果。
分析:BFS逐层删除。每层删除一个括号,找到有效的就停止。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public List<String> removeInvalidParentheses (String s) { List<String> res = new ArrayList <>(); Queue<String> queue = new LinkedList <>(); Set<String> visited = new HashSet <>(); queue.offer(s); visited.add(s); boolean found = false ; while (!queue.isEmpty()) { String curr = queue.poll(); if (isValid(curr)) { res.add(curr); found = true ; } if (found) continue ; for (int i = 0 ; i < curr.length(); i++) { if (curr.charAt(i) != '(' && curr.charAt(i) != ')' ) continue ; String next = curr.substring(0 , i) + curr.substring(i + 1 ); if (visited.add(next)) queue.offer(next); } } return res; } private boolean isValid (String s) { int count = 0 ; for (char c : s.toCharArray()) { if (c == '(' ) count++; else if (c == ')' ) { if (--count < 0 ) return false ; } } return count == 0 ; }
时间复杂度:O(2^n),空间复杂度:O(2^n)
181. 🟡 最大二叉树(Maximum Binary Tree)- LeetCode 654
题目:给定不含重复元素的整数数组,构建最大二叉树(根是最大值,左右子树递归构建)。
分析:递归。找到最大值作为根,左边递归构建左子树,右边递归构建右子树。
1 2 3 4 5 6 7 8 9 10 11 12 13 public TreeNode constructMaximumBinaryTree (int [] nums) { return build(nums, 0 , nums.length - 1 ); } private TreeNode build (int [] nums, int lo, int hi) { if (lo > hi) return null ; int maxIdx = lo; for (int i = lo + 1 ; i <= hi; i++) if (nums[i] > nums[maxIdx]) maxIdx = i; TreeNode root = new TreeNode (nums[maxIdx]); root.left = build(nums, lo, maxIdx - 1 ); root.right = build(nums, maxIdx + 1 , hi); return root; }
时间复杂度:平均O(n·log n),最坏O(n²),空间复杂度:O(n)
182. 🟡 有效的完全平方数(Valid Perfect Square)- LeetCode 367
题目:不使用内置函数,判断一个正整数是否是完全平方数。
分析:二分查找。
1 2 3 4 5 6 7 8 9 10 11 public boolean isPerfectSquare (int num) { long lo = 1 , hi = num; while (lo <= hi) { long mid = lo + (hi - lo) / 2 ; long sq = mid * mid; if (sq == num) return true ; else if (sq < num) lo = mid + 1 ; else hi = mid - 1 ; } return false ; }
时间复杂度:O(log n),空间复杂度:O(1)
183. 🟡 x的平方根(Sqrt(x))- LeetCode 69
题目:计算并返回x的算术平方根(整数部分)。
分析:二分查找。找最大的mid使得mid*mid <= x。
1 2 3 4 5 6 7 8 9 public int mySqrt (int x) { long lo = 0 , hi = x; while (lo <= hi) { long mid = lo + (hi - lo) / 2 ; if (mid * mid <= x) lo = mid + 1 ; else hi = mid - 1 ; } return (int ) hi; }
时间复杂度:O(log n),空间复杂度:O(1)
184. 🟡 最长公共前缀(Longest Common Prefix)- LeetCode 14
题目:找出字符串数组中的最长公共前缀。
分析:纵向扫描。逐字符比较所有字符串的同一位置。
1 2 3 4 5 6 7 8 public String longestCommonPrefix (String[] strs) { for (int i = 0 ; i < strs[0 ].length(); i++) { char c = strs[0 ].charAt(i); for (int j = 1 ; j < strs.length; j++) if (i >= strs[j].length() || strs[j].charAt(i) != c) return strs[0 ].substring(0 , i); } return strs[0 ]; }
时间复杂度:O(S),S为所有字符总数,空间复杂度:O(1)
185. 🟡 回文数(Palindrome Number)- LeetCode 9
题目:判断一个整数是否是回文数,不将整数转为字符串。
分析:反转后半部分数字与前半部分比较。
1 2 3 4 5 6 public boolean isPalindrome (int x) { if (x < 0 || (x % 10 == 0 && x != 0 )) return false ; int rev = 0 ; while (x > rev) { rev = rev * 10 + x % 10 ; x /= 10 ; } return x == rev || x == rev / 10 ; }
时间复杂度:O(log n),空间复杂度:O(1)
186. 🟡 反转整数(Reverse Integer)- LeetCode 7
题目:给定32位有符号整数,反转其数字。溢出返回0。
分析:逐位取出,检查溢出。
1 2 3 4 5 6 7 8 9 10 public int reverse (int x) { int rev = 0 ; while (x != 0 ) { int digit = x % 10 ; if (rev > Integer.MAX_VALUE / 10 || rev < Integer.MIN_VALUE / 10 ) return 0 ; rev = rev * 10 + digit; x /= 10 ; } return rev; }
时间复杂度:O(log n),空间复杂度:O(1)
187. 🟡 二进制求和(Add Binary)- LeetCode 67
题目:给定两个二进制字符串,返回它们的和(也是二进制字符串)。
分析:从末尾开始逐位相加,处理进位。
1 2 3 4 5 6 7 8 9 10 11 12 public String addBinary (String a, String b) { StringBuilder sb = new StringBuilder (); int i = a.length() - 1 , j = b.length() - 1 , carry = 0 ; while (i >= 0 || j >= 0 || carry > 0 ) { int sum = carry; if (i >= 0 ) sum += a.charAt(i--) - '0' ; if (j >= 0 ) sum += b.charAt(j--) - '0' ; sb.append(sum % 2 ); carry = sum / 2 ; } return sb.reverse().toString(); }
时间复杂度:O(max(m,n)),空间复杂度:O(max(m,n))
188. 🟡 删除排序数组中的重复项(Remove Duplicates)- LeetCode 26
题目:原地删除排序数组中的重复项,返回新长度。
分析:双指针。慢指针记录不重复元素的位置。
1 2 3 4 5 6 public int removeDuplicates (int [] nums) { int j = 0 ; for (int i = 1 ; i < nums.length; i++) if (nums[i] != nums[j]) nums[++j] = nums[i]; return j + 1 ; }
时间复杂度:O(n),空间复杂度:O(1)
189. 🟡 移除元素(Remove Element)- LeetCode 27
题目:原地移除数组中所有等于val的元素,返回新长度。
分析:双指针。
1 2 3 4 5 public int removeElement (int [] nums, int val) { int j = 0 ; for (int n : nums) if (n != val) nums[j++] = n; return j; }
时间复杂度:O(n),空间复杂度:O(1)
190. 🟡 实现strStr()(Find the Index of the First Occurrence)- LeetCode 28
题目:在haystack中找到needle第一次出现的位置。
分析:KMP算法或直接滑动窗口。面试中直接法即可。
1 2 3 4 5 6 7 8 9 public int strStr (String haystack, String needle) { int m = haystack.length(), n = needle.length(); for (int i = 0 ; i <= m - n; i++) { int j = 0 ; while (j < n && haystack.charAt(i + j) == needle.charAt(j)) j++; if (j == n) return i; } return -1 ; }
时间复杂度:O(m·n),空间复杂度:O(1)
191. 🟡 缺失的第一个正数(First Missing Positive)- LeetCode 41
题目:给定未排序的整数数组,找出其中没有出现的最小正整数,要求O(n)时间O(1)空间。
分析:原地哈希。将每个数放到它应该在的位置(nums[i]放到nums[nums[i]-1])。
1 2 3 4 5 6 7 8 9 public int firstMissingPositive (int [] nums) { int n = nums.length; for (int i = 0 ; i < n; i++) while (nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1 ] != nums[i]) { int tmp = nums[nums[i] - 1 ]; nums[nums[i] - 1 ] = nums[i]; nums[i] = tmp; } for (int i = 0 ; i < n; i++) if (nums[i] != i + 1 ) return i + 1 ; return n + 1 ; }
时间复杂度:O(n),空间复杂度:O(1)
192. 🟡 外观数列(Count and Say)- LeetCode 38
题目:给定正整数n,输出外观数列的第n项。
分析:迭代模拟。对上一项进行”读”操作生成下一项。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public String countAndSay (int n) { String s = "1" ; for (int i = 1 ; i < n; i++) { StringBuilder sb = new StringBuilder (); int count = 1 ; for (int j = 1 ; j < s.length(); j++) { if (s.charAt(j) == s.charAt(j - 1 )) count++; else { sb.append(count).append(s.charAt(j - 1 )); count = 1 ; } } sb.append(count).append(s.charAt(s.length() - 1 )); s = sb.toString(); } return s; }
时间复杂度:O(n·m),m为字符串长度,空间复杂度:O(m)
193. 🟡 解码方法(Decode Ways)- LeetCode 91
题目:给定数字字符串,计算有多少种解码方式(A=1, B=2, …, Z=26)。
分析:动态规划。dp[i]表示前i个字符的解码方式数。
1 2 3 4 5 6 7 8 9 10 11 12 13 public int numDecodings (String s) { if (s.charAt(0 ) == '0' ) return 0 ; int n = s.length(); int prev2 = 1 , prev1 = 1 ; for (int i = 1 ; i < n; i++) { int curr = 0 ; if (s.charAt(i) != '0' ) curr = prev1; int two = Integer.parseInt(s.substring(i - 1 , i + 1 )); if (two >= 10 && two <= 26 ) curr += prev2; prev2 = prev1; prev1 = curr; } return prev1; }
时间复杂度:O(n),空间复杂度:O(1)
194. 🟡 交错字符串(Interleaving String)- LeetCode 97
题目:判断s3是否由s1和s2交错组成。
分析:二维DP。dp[i][j]表示s1前i个和s2前j个能否交错组成s3前i+j个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public boolean isInterleave (String s1, String s2, String s3) { int m = s1.length(), n = s2.length(); if (m + n != s3.length()) return false ; boolean [] dp = new boolean [n + 1 ]; for (int i = 0 ; i <= m; i++) for (int j = 0 ; j <= n; j++) { if (i == 0 && j == 0 ) dp[j] = true ; else if (i == 0 ) dp[j] = dp[j - 1 ] && s2.charAt(j - 1 ) == s3.charAt(j - 1 ); else if (j == 0 ) dp[j] = dp[j] && s1.charAt(i - 1 ) == s3.charAt(i - 1 ); else dp[j] = (dp[j] && s1.charAt(i - 1 ) == s3.charAt(i + j - 1 )) || (dp[j - 1 ] && s2.charAt(j - 1 ) == s3.charAt(i + j - 1 )); } return dp[n]; }
时间复杂度:O(m·n),空间复杂度:O(n)
195. 🟡 不同路径 II(Unique Paths II)- LeetCode 63
题目:网格中有障碍物,从左上角到右下角有多少条不同路径。
分析:动态规划。障碍物位置dp值为0。
1 2 3 4 5 6 7 8 9 10 11 public int uniquePathsWithObstacles (int [][] grid) { int n = grid[0 ].length; int [] dp = new int [n]; dp[0 ] = 1 ; for (int [] row : grid) for (int j = 0 ; j < n; j++) { if (row[j] == 1 ) dp[j] = 0 ; else if (j > 0 ) dp[j] += dp[j - 1 ]; } return dp[n - 1 ]; }
时间复杂度:O(m·n),空间复杂度:O(n)
196. 🟡 加一(Plus One)- LeetCode 66
题目:给定一个由整数组成的非空数组表示的非负整数,加一。
分析:从末尾开始加一,处理进位。
1 2 3 4 5 6 7 8 9 public int [] plusOne(int [] digits) { for (int i = digits.length - 1 ; i >= 0 ; i--) { if (digits[i] < 9 ) { digits[i]++; return digits; } digits[i] = 0 ; } int [] result = new int [digits.length + 1 ]; result[0 ] = 1 ; return result; }
时间复杂度:O(n),空间复杂度:O(1)
197. 🟡 杨辉三角(Pascal’s Triangle)- LeetCode 118
题目:给定行数,生成杨辉三角。
分析:每行第一个和最后一个是1,中间的数等于上一行相邻两数之和。
1 2 3 4 5 6 7 8 9 10 public List<List<Integer>> generate (int numRows) { List<List<Integer>> res = new ArrayList <>(); for (int i = 0 ; i < numRows; i++) { List<Integer> row = new ArrayList <>(); for (int j = 0 ; j <= i; j++) row.add(j == 0 || j == i ? 1 : res.get(i - 1 ).get(j - 1 ) + res.get(i - 1 ).get(j)); res.add(row); } return res; }
时间复杂度:O(n²),空间复杂度:O(n²)
198. 🟡 二叉树的前序遍历(Binary Tree Preorder Traversal)- LeetCode 144
题目:给定二叉树,返回前序遍历结果(迭代实现)。
分析:用栈模拟。先压右子节点再压左子节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 public List<Integer> preorderTraversal (TreeNode root) { List<Integer> res = new ArrayList <>(); if (root == null ) return res; Deque<TreeNode> stack = new ArrayDeque <>(); stack.push(root); while (!stack.isEmpty()) { TreeNode node = stack.pop(); res.add(node.val); if (node.right != null ) stack.push(node.right); if (node.left != null ) stack.push(node.left); } return res; }
时间复杂度:O(n),空间复杂度:O(n)
199. 🟡 有效的括号字符串(Valid Parenthesis String)- LeetCode 678
题目:给定只包含’(‘、’)’和’‘的字符串,’ ‘可以是’(‘、’)’或空,判断是否有效。
分析:贪心。维护未匹配左括号数量的最小值和最大值范围。
1 2 3 4 5 6 7 8 9 10 11 public boolean checkValidString (String s) { int lo = 0 , hi = 0 ; for (char c : s.toCharArray()) { if (c == '(' ) { lo++; hi++; } else if (c == ')' ) { lo--; hi--; } else { lo--; hi++; } if (hi < 0 ) return false ; lo = Math.max(lo, 0 ); } return lo == 0 ; }
时间复杂度:O(n),空间复杂度:O(1)
200. 🟡 图的深拷贝变体 - 太平洋大西洋水流问题(Pacific Atlantic Water Flow)- LeetCode 417
题目:m×n矩阵表示海拔,水可以向四个方向流向相同或更低的格子。左上边界是太平洋,右下边界是大西洋。找出能同时流向两个大洋的格子。
分析:反向思维。从两个大洋的边界出发DFS/BFS,标记能到达的格子,取交集。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public List<List<Integer>> pacificAtlantic (int [][] heights) { int m = heights.length, n = heights[0 ].length; boolean [][] pacific = new boolean [m][n], atlantic = new boolean [m][n]; for (int i = 0 ; i < m; i++) { dfs(heights, pacific, i, 0 ); dfs(heights, atlantic, i, n - 1 ); } for (int j = 0 ; j < n; j++) { dfs(heights, pacific, 0 , j); dfs(heights, atlantic, m - 1 , j); } List<List<Integer>> res = new ArrayList <>(); for (int i = 0 ; i < m; i++) for (int j = 0 ; j < n; j++) if (pacific[i][j] && atlantic[i][j]) res.add(Arrays.asList(i, j)); return res; } private void dfs (int [][] heights, boolean [][] visited, int i, int j) { visited[i][j] = true ; int [][] dirs = {{1 ,0 },{-1 ,0 },{0 ,1 },{0 ,-1 }}; for (int [] d : dirs) { int ni = i + d[0 ], nj = j + d[1 ]; if (ni >= 0 && ni < heights.length && nj >= 0 && nj < heights[0 ].length && !visited[ni][nj] && heights[ni][nj] >= heights[i][j]) dfs(heights, visited, ni, nj); } }
时间复杂度:O(m·n),空间复杂度:O(m·n)