Skip to content

LeetCode Hot 100 精讲

本章目标:按主题分组梳理 LeetCode Hot 100 高频面试题,每题给出核心思路、关键代码与复杂度。

你将学到

  • Hot 100 的按主题分类
  • 每道题的核心思路与代码模板
  • 快速识别题型的方法
  • 高频题的最优解

使用方法

  1. 第一遍:看一句话思路,尝试自己写。
  2. 第二遍:对比代码,理解差异。
  3. 第三遍:闭卷默写,限时 20 分钟一道 Medium。

哈希表

1 · 两数之和(Easy)

思路:哈希表存 target-num,扫一遍即可。

python
def twoSum(nums, target):
    seen = {}
    for i, num in enumerate(nums):
        if target - num in seen:
            return [seen[target - num], i]
        seen[num] = i

时间 O(n),空间 O(n)。

变体

  • 两数之和 II(有序数组):双指针 O(n)。
  • 三数之和:排序 + 双指针 O(n²)。
  • 四数之和:排序 + 双指针 + 剪枝 O(n³)。

49 · 字母异位词分组(Medium)

思路:排序后的字符串做 key,原字符串归到对应组。

python
from collections import defaultdict
def groupAnagrams(strs):
    groups = defaultdict(list)
    for s in strs:
        key = ''.join(sorted(s))
        groups[key].append(s)
    return list(groups.values())

时间 O(n·k log k),n 是字符串数,k 是最长串长度。

优化:用计数(26 维向量)做 key,时间 O(n·k)。

128 · 最长连续序列(Medium)

思路:Set 存全部,只从序列起点(num-1 不在 Set 中)开始数。

python
def longestConsecutive(nums):
    num_set = set(nums)
    longest = 0
    for num in num_set:
        if num - 1 not in num_set:  # 起点
            cur = num
            length = 1
            while cur + 1 in num_set:
                cur += 1
                length += 1
            longest = max(longest, length)
    return longest

时间 O(n)(每个元素最多访问 2 次)。


双指针

283 · 移动零(Easy)

思路:快慢指针交换,慢指针指向下一个非零应放的位置。

python
def moveZeroes(nums):
    slow = 0
    for fast in range(len(nums)):
        if nums[fast] != 0:
            nums[slow], nums[fast] = nums[fast], nums[slow]
            slow += 1

时间 O(n),空间 O(1)。

11 · 盛最多水的容器(Medium)

思路:对撞指针,每次移动较短的一边(移动较长边不可能更大)。

python
def maxArea(height):
    l, r = 0, len(height) - 1
    ans = 0
    while l < r:
        ans = max(ans, min(height[l], height[r]) * (r - l))
        if height[l] < height[r]:
            l += 1
        else:
            r -= 1
    return ans

时间 O(n),空间 O(1)。

15 · 三数之和(Medium)

思路:排序 + 固定一个数 + 双指针找两数之和。

python
def threeSum(nums):
    nums.sort()
    ans = []
    n = len(nums)
    for i in range(n - 2):
        if i > 0 and nums[i] == nums[i-1]: continue  # 去重
        l, r = i + 1, n - 1
        while l < r:
            s = nums[i] + nums[l] + nums[r]
            if s < 0: l += 1
            elif s > 0: r -= 1
            else:
                ans.append([nums[i], nums[l], nums[r]])
                while l < r and nums[l] == nums[l+1]: l += 1  # 去重
                while l < r and nums[r] == nums[r-1]: r -= 1
                l += 1; r -= 1
    return ans

时间 O(n²),空间 O(1)(不含输出)。

42 · 接雨水(Hard)

思路 1(双指针):维护 leftMax、rightMax,从较小端推进。

python
def trap(height):
    l, r = 0, len(height) - 1
    lmax = rmax = 0
    ans = 0
    while l < r:
        if height[l] < height[r]:
            if height[l] >= lmax: lmax = height[l]
            else: ans += lmax - height[l]
            l += 1
        else:
            if height[r] >= rmax: rmax = height[r]
            else: ans += rmax - height[r]
            r -= 1
    return ans

时间 O(n),空间 O(1)。

思路 2(单调栈):维护递减栈,遇到更高柱子时弹出栈顶并计算。

思路 3(DP 预处理):预处理 leftMax[]、rightMax[],逐项求和。


滑动窗口

3 · 无重复字符的最长子串(Medium)

思路:窗口 + 哈希,遇重复缩左。

python
def lengthOfLongestSubstring(s):
    seen = {}
    l = 0
    ans = 0
    for r, c in enumerate(s):
        if c in seen and seen[c] >= l:
            l = seen[c] + 1
        seen[c] = r
        ans = max(ans, r - l + 1)
    return ans

时间 O(n),空间 O(min(n, 字符集大小))。

438 · 找到所有异位词(Medium)

思路:定长窗口比较频率(用计数数组或排序后比较)。

python
from collections import Counter
def findAnagrams(s, p):
    ans = []
    pc = Counter(p)
    sc = Counter()
    k = len(p)
    for i, c in enumerate(s):
        sc[c] += 1
        if i >= k:
            sc[s[i-k]] -= 1
            if sc[s[i-k]] == 0: del sc[s[i-k]]
        if sc == pc:
            ans.append(i - k + 1)
    return ans

时间 O(n),空间 O(字符集大小)。


子串

560 · 和为 K 的子数组(Medium)

思路:前缀和 + 哈希。prefix[j] - prefix[i] = kprefix[i] = prefix[j] - k

python
from collections import defaultdict
def subarraySum(nums, k):
    cnt = defaultdict(int)
    cnt[0] = 1
    prefix = 0
    ans = 0
    for num in nums:
        prefix += num
        ans += cnt[prefix - k]
        cnt[prefix] += 1
    return ans

时间 O(n),空间 O(n)。

注意:不能用滑动窗口(数组可能有负数)。

239 · 滑动窗口最大值(Hard)

思路:单调递减队列,队首始终是窗口最大值。

python
from collections import deque
def maxSlidingWindow(nums, k):
    dq = deque()  # 存索引
    ans = []
    for i, num in enumerate(nums):
        while dq and nums[dq[-1]] <= num:
            dq.pop()
        dq.append(i)
        if dq[0] <= i - k:
            dq.popleft()
        if i >= k - 1:
            ans.append(nums[dq[0]])
    return ans

时间 O(n),空间 O(k)。

76 · 最小覆盖子串(Hard)

思路:不定长窗口 + 计数。

python
from collections import Counter
def minWindow(s, t):
    need = Counter(t)
    missing = len(t)
    l = 0
    start, end = 0, float('inf')
    for r, c in enumerate(s):
        if need[c] > 0: missing -= 1
        need[c] -= 1
        while missing == 0:
            if r - l < end - start:
                start, end = l, r
            need[s[l]] += 1
            if need[s[l]] > 0: missing += 1
            l += 1
    return s[start:end+1] if end != float('inf') else ""

时间 O(|s| + |t|),空间 O(字符集大小)。


普通数组

53 · 最大子数组和(Medium)

思路:DP 或贪心,维护以当前元素结尾的最大和。

python
def maxSubArray(nums):
    cur = ans = nums[0]
    for num in nums[1:]:
        cur = max(num, cur + num)
        ans = max(ans, cur)
    return ans

时间 O(n),空间 O(1)。

变体:返回子数组本身?维护起止下标。

56 · 合并区间(Medium)

思路:按左端点排序后扫描。

python
def merge(intervals):
    intervals.sort()
    ans = [intervals[0]]
    for s, e in intervals[1:]:
        if s <= ans[-1][1]:
            ans[-1][1] = max(ans[-1][1], e)
        else:
            ans.append([s, e])
    return ans

时间 O(n log n),空间 O(1)(不含输出)。

189 · 轮转数组(Medium)

思路:三次反转。

python
def rotate(nums, k):
    n = len(nums)
    k %= n
    nums.reverse()
    nums[:k] = reversed(nums[:k])
    nums[k:] = reversed(nums[k:])

时间 O(n),空间 O(1)。

238 · 除自身以外数组的乘积(Medium)

思路:左右前缀积。

python
def productExceptSelf(nums):
    n = len(nums)
    ans = [1] * n
    left = 1
    for i in range(n):
        ans[i] = left
        left *= nums[i]
    right = 1
    for i in range(n-1, -1, -1):
        ans[i] *= right
        right *= nums[i]
    return ans

时间 O(n),空间 O(1)(不含输出)。

41 · 缺失的第一个正数(Hard)

思路:原地哈希。把 nums[i] 放到下标 nums[i]-1 的位置。

python
def firstMissingPositive(nums):
    n = len(nums)
    for i in range(n):
        while 1 <= nums[i] <= n and nums[nums[i]-1] != nums[i]:
            nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1]
    for i in range(n):
        if nums[i] != i + 1:
            return i + 1
    return n + 1

时间 O(n),空间 O(1)。


链表

160 · 相交链表(Easy)

思路:双指针走完自己走对方的路径,必然在交点相遇。

python
def getIntersectionNode(headA, headB):
    pa, pb = headA, headB
    while pa != pb:
        pa = pa.next if pa else headB
        pb = pb.next if pb else headA
    return pa

时间 O(m+n),空间 O(1)。

206 · 反转链表(Easy)

python
def reverseList(head):
    prev, cur = None, head
    while cur:
        nxt = cur.next
        cur.next = prev
        prev, cur = cur, nxt
    return prev

时间 O(n),空间 O(1)。

234 · 回文链表(Easy)

思路:快慢找中点 + 翻转后半 + 比较。

141 · 环形链表(Easy)

思路:快慢指针,相遇则有环。

142 · 环形链表 II(Medium)

思路:Floyd 找入口。相遇后,一指针回到头,两指针同速前进再次相遇即为入口。

python
def detectCycle(head):
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            # 找入口
            slow = head
            while slow != fast:
                slow = slow.next
                fast = fast.next
            return slow
    return None

21 · 合并两个有序链表(Easy)

思路:虚拟头节点 + 双指针。

2 · 两数相加(Medium)

思路:模拟进位。

19 · 删除倒数第 N 个节点(Medium)

思路:快慢指针,快先走 N 步。

25 · K 个一组翻转链表(Hard)

思路:分段翻转,注意前后连接。

138 · 随机链表的复制(Medium)

思路:哈希映射或节点拼接(在原节点后插入复制节点)。

148 · 排序链表(Hard)

思路:归并排序。O(n log n) 时间,O(log n) 空间(递归栈)。

23 · 合并 K 个升序链表(Hard)

思路:小根堆,每次弹出最小元素的链表。

python
import heapq
def mergeKLists(lists):
    heap = []
    for i, node in enumerate(lists):
        if node:
            heapq.heappush(heap, (node.val, i, node))
    dummy = ListNode()
    cur = dummy
    while heap:
        val, i, node = heapq.heappop(heap)
        cur.next = node
        cur = cur.next
        if node.next:
            heapq.heappush(heap, (node.next.val, i, node.next))
    return dummy.next

时间 O(N log k),N 是总节点数,k 是链表数。

146 · LRU 缓存(Medium)

详见 LRU 缓存专题


二叉树

94 · 中序遍历(Easy)

迭代版(用栈):

python
def inorderTraversal(root):
    ans, stack = [], []
    while root or stack:
        while root:
            stack.append(root)
            root = root.left
        root = stack.pop()
        ans.append(root.val)
        root = root.right
    return ans

104 · 最大深度(Easy)

python
def maxDepth(root):
    if not root: return 0
    return 1 + max(maxDepth(root.left), maxDepth(root.right))

226 · 翻转二叉树(Easy)

python
def invertTree(root):
    if not root: return None
    root.left, root.right = invertTree(root.right), invertTree(root.left)
    return root

101 · 对称二叉树(Easy)

python
def isSymmetric(root):
    def check(a, b):
        if not a and not b: return True
        if not a or not b: return False
        return a.val == b.val and check(a.left, b.right) and check(a.right, b.left)
    return check(root.left, root.right) if root else True

543 · 二叉树直径(Easy)

思路:DFS 返回深度,过程中维护全局最大直径。

python
def diameterOfBinaryTree(root):
    ans = 0
    def depth(node):
        nonlocal ans
        if not node: return 0
        l = depth(node.left)
        r = depth(node.right)
        ans = max(ans, l + r)
        return 1 + max(l, r)
    depth(root)
    return ans

102 · 层序遍历(Medium)

python
from collections import deque
def levelOrder(root):
    if not root: return []
    ans = []
    q = deque([root])
    while q:
        level = []
        for _ in range(len(q)):
            node = q.popleft()
            level.append(node.val)
            if node.left: q.append(node.left)
            if node.right: q.append(node.right)
        ans.append(level)
    return ans

108 · 有序数组转 BST(Medium)

思路:取中点为根,递归左右子数组。

98 · 验证 BST(Medium)

思路:中序遍历应严格递增,或传递上下界。

python
def isValidBST(root):
    def check(node, lo, hi):
        if not node: return True
        if not (lo < node.val < hi): return False
        return check(node.left, lo, node.val) and check(node.right, node.val, hi)
    return check(root, float('-inf'), float('inf'))

230 · BST 第 K 小(Medium)

思路:中序遍历到第 K 个。

199 · 二叉树右视图(Medium)

思路:层序取每层最后。

114 · 二叉树展开为链表(Medium)

思路:右子树接到左子树最右,左子树作为新右子树,递归。

105 · 前序+中序构造二叉树(Medium)

思路:前序定根,中序分左右。

437 · 路径总和 III(Medium)

思路:前缀和 + DFS。

236 · 最近公共祖先(Medium)

python
def lowestCommonAncestor(root, p, q):
    if not root or root == p or root == q:
        return root
    left = lowestCommonAncestor(root.left, p, q)
    right = lowestCommonAncestor(root.right, p, q)
    if left and right: return root
    return left or right

124 · 最大路径和(Hard)

思路:DFS 维护全局最大,返回"单边最大贡献"。

python
def maxPathSum(root):
    ans = float('-inf')
    def gain(node):
        nonlocal ans
        if not node: return 0
        l = max(gain(node.left), 0)
        r = max(gain(node.right), 0)
        ans = max(ans, node.val + l + r)
        return node.val + max(l, r)
    gain(root)
    return ans

图论

200 · 岛屿数量(Medium)

思路:DFS/BFS 标记。遍历每个格子,遇 1 启动 DFS 把连通的 1 全部置 0。

python
def numIslands(grid):
    if not grid: return 0
    m, n = len(grid), len(grid[0])
    count = 0
    def dfs(i, j):
        if i < 0 or i >= m or j < 0 or j >= n or grid[i][j] != '1':
            return
        grid[i][j] = '0'
        for di, dj in [(1,0),(-1,0),(0,1),(0,-1)]:
            dfs(i+di, j+dj)
    for i in range(m):
        for j in range(n):
            if grid[i][j] == '1':
                count += 1
                dfs(i, j)
    return count

994 · 腐烂的橘子(Medium)

思路:多源 BFS。

207 · 课程表(Medium)

思路:拓扑排序判环(BFS 入度法或 DFS 三色标记)。

python
from collections import deque, defaultdict
def canFinish(numCourses, prerequisites):
    g = defaultdict(list)
    indeg = [0] * numCourses
    for a, b in prerequisites:
        g[b].append(a)
        indeg[a] += 1
    q = deque([i for i in range(numCourses) if indeg[i] == 0])
    count = 0
    while q:
        node = q.popleft()
        count += 1
        for nei in g[node]:
            indeg[nei] -= 1
            if indeg[nei] == 0:
                q.append(nei)
    return count == numCourses

208 · 实现 Trie(Medium)

思路:字典树。

python
class Trie:
    def __init__(self):
        self.children = {}
        self.is_end = False
    def insert(self, word):
        node = self
        for c in word:
            if c not in node.children:
                node.children[c] = Trie()
            node = node.children[c]
        node.is_end = True
    def search(self, word):
        node = self._find(word)
        return node is not None and node.is_end
    def startsWith(self, prefix):
        return self._find(prefix) is not None
    def _find(self, prefix):
        node = self
        for c in prefix:
            if c not in node.children: return None
            node = node.children[c]
        return node

回溯

46 · 全排列(Medium)

python
def permute(nums):
    ans = []
    def backtrack(path, used):
        if len(path) == len(nums):
            ans.append(path[:])
            return
        for i in range(len(nums)):
            if not used[i]:
                used[i] = True
                path.append(nums[i])
                backtrack(path, used)
                path.pop()
                used[i] = False
    backtrack([], [False]*len(nums))
    return ans

78 · 子集(Medium)

python
def subsets(nums):
    ans = []
    def backtrack(start, path):
        ans.append(path[:])
        for i in range(start, len(nums)):
            path.append(nums[i])
            backtrack(i+1, path)
            path.pop()
    backtrack(0, [])
    return ans

17 · 电话号码字母组合(Medium)

思路:回溯 + 数字到字母的映射。

39 · 组合总和(Medium)

思路:回溯,可重复选(递归时 i 不 +1)。

22 · 括号生成(Medium)

思路:回溯 + 剪枝(左括号数 < n,右括号数 < 左括号数)。

python
def generateParenthesis(n):
    ans = []
    def backtrack(s, l, r):
        if len(s) == 2 * n:
            ans.append(s)
            return
        if l < n: backtrack(s + '(', l + 1, r)
        if r < l: backtrack(s + ')', l, r + 1)
    backtrack('', 0, 0)
    return ans

79 · 单词搜索(Medium)

思路:DFS + 回溯。从每个格子尝试匹配首字母。

131 · 分割回文串(Medium)

思路:回溯切割 + 回文判断。

51 · N 皇后(Hard)

思路:逐行回溯,对角线去重(用集合记录列、主对角、副对角)。

python
def solveNQueens(n):
    ans = []
    cols, diag1, diag2 = set(), set(), set()
    board = [['.'] * n for _ in range(n)]
    def backtrack(r):
        if r == n:
            ans.append([''.join(row) for row in board])
            return
        for c in range(n):
            if c in cols or r-c in diag1 or r+c in diag2:
                continue
            board[r][c] = 'Q'
            cols.add(c); diag1.add(r-c); diag2.add(r+c)
            backtrack(r + 1)
            board[r][c] = '.'
            cols.discard(c); diag1.discard(r-c); diag2.discard(r+c)
    backtrack(0)
    return ans

二分查找

35 · 搜索插入位置(Easy)

思路:标准 lowerBound。

python
def searchInsert(nums, target):
    l, r = 0, len(nums)
    while l < r:
        m = (l + r) // 2
        if nums[m] < target: l = m + 1
        else: r = m
    return l

74 · 搜索二维矩阵(Medium)

思路:展平后二分。

34 · 查找范围(Medium)

思路:左右边界各二分一次。

python
def searchRange(nums, target):
    def find_first():
        l, r = 0, len(nums)
        while l < r:
            m = (l + r) // 2
            if nums[m] < target: l = m + 1
            else: r = m
        return l
    def find_last():
        l, r = 0, len(nums)
        while l < r:
            m = (l + r) // 2
            if nums[m] <= target: l = m + 1
            else: r = m
        return l - 1
    first = find_first()
    if first == len(nums) or nums[first] != target:
        return [-1, -1]
    return [first, find_last()]

33 · 搜索旋转数组(Medium)

思路:判断哪半有序。

python
def search(nums, target):
    l, r = 0, len(nums) - 1
    while l <= r:
        m = (l + r) // 2
        if nums[m] == target: return m
        if nums[l] <= nums[m]:  # 左半有序
            if nums[l] <= target < nums[m]: r = m - 1
            else: l = m + 1
        else:  # 右半有序
            if nums[m] < target <= nums[r]: l = m + 1
            else: r = m - 1
    return -1

153 · 旋转数组最小值(Medium)

思路:二分比较 mid 与 right。

4 · 寻找两个正序数组中位数(Hard)

思路:二分切分点,O(log(min(m, n)))。


20 · 有效的括号(Easy)

python
def isValid(s):
    pair = {')': '(', ']': '[', '}': '{'}
    stack = []
    for c in s:
        if c in '([{': stack.append(c)
        elif not stack or stack.pop() != pair[c]: return False
    return not stack

155 · 最小栈(Easy)

思路:辅助栈同步维护当前最小。

394 · 字符串解码(Medium)

思路:栈处理嵌套(数字 + 字符串)。

739 · 每日温度(Medium)

思路:单调递减栈。

python
def dailyTemperatures(temperatures):
    n = len(temperatures)
    ans = [0] * n
    stack = []
    for i in range(n):
        while stack and temperatures[stack[-1]] < temperatures[i]:
            j = stack.pop()
            ans[j] = i - j
        stack.append(i)
    return ans

84 · 柱状图最大矩形(Hard)

思路:单调递增栈,弹出时计算以栈顶为高的矩形。

python
def largestRectangleArea(heights):
    stack = []
    ans = 0
    heights.append(0)  # 哨兵
    for i in range(len(heights)):
        while stack and heights[stack[-1]] > heights[i]:
            h = heights[stack.pop()]
            w = i if not stack else i - stack[-1] - 1
            ans = max(ans, h * w)
        stack.append(i)
    return ans

215 · 第 K 大(Medium)

思路:小根堆维护 K 个元素,或快速选择。

python
import heapq
def findKthLargest(nums, k):
    heap = nums[:k]
    heapq.heapify(heap)
    for num in nums[k:]:
        if num > heap[0]:
            heapq.heapreplace(heap, num)
    return heap[0]

快速选择(平均 O(n)):

python
import random
def findKthLargest(nums, k):
    def partition(l, r):
        pivot = nums[r]
        i = l
        for j in range(l, r):
            if nums[j] > pivot:  # 大到小
                nums[i], nums[j] = nums[j], nums[i]
                i += 1
        nums[i], nums[r] = nums[r], nums[i]
        return i
    l, r = 0, len(nums) - 1
    k -= 1
    while True:
        p = partition(l, r)
        if p == k: return nums[p]
        elif p < k: l = p + 1
        else: r = p - 1

347 · 前 K 高频(Medium)

思路:频率统计 + 堆 / 桶排序。

295 · 数据流中位数(Hard)

思路:大根堆 + 小根堆,左边最大 ≤ 右边最小。

python
import heapq
class MedianFinder:
    def __init__(self):
        self.lo = []  # 大根堆(存负值)
        self.hi = []  # 小根堆
    def addNum(self, num):
        heapq.heappush(self.lo, -num)
        heapq.heappush(self.hi, -heapq.heappop(self.lo))
        if len(self.lo) < len(self.hi):
            heapq.heappush(self.lo, -heapq.heappop(self.hi))
    def findMedian(self):
        if len(self.lo) > len(self.hi):
            return -self.lo[0]
        return (-self.lo[0] + self.hi[0]) / 2

贪心

121 · 买卖股票最佳时机(Easy)

思路:维护历史最小值。

python
def maxProfit(prices):
    min_price = float('inf')
    ans = 0
    for p in prices:
        min_price = min(min_price, p)
        ans = max(ans, p - min_price)
    return ans

55 · 跳跃游戏(Medium)

思路:维护最远可达。

python
def canJump(nums):
    far = 0
    for i in range(len(nums)):
        if i > far: return False
        far = max(far, i + nums[i])
    return True

45 · 跳跃游戏 II(Medium)

思路:贪心最少跳跃,维护当前轮最远。

763 · 划分字母区间(Medium)

思路:记录每个字母最后位置。


动态规划

70 · 爬楼梯(Easy)

思路dp[i] = dp[i-1] + dp[i-2],本质斐波那契。可空间压缩到 O(1)。

118 · 杨辉三角(Easy)

python
def generate(numRows):
    ans = [[1]]
    for i in range(1, numRows):
        row = [1]
        for j in range(1, i):
            row.append(ans[-1][j-1] + ans[-1][j])
        row.append(1)
        ans.append(row)
    return ans

198 · 打家劫舍(Medium)

python
def rob(nums):
    prev, curr = 0, 0
    for num in nums:
        prev, curr = curr, max(curr, prev + num)
    return curr

279 · 完全平方数(Medium)

思路:完全背包。

python
def numSquares(n):
    dp = [float('inf')] * (n + 1)
    dp[0] = 0
    for i in range(1, n + 1):
        j = 1
        while j * j <= i:
            dp[i] = min(dp[i], dp[i - j*j] + 1)
            j += 1
    return dp[n]

322 · 零钱兑换(Medium)

思路:完全背包。

python
def coinChange(coins, amount):
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0
    for i in range(1, amount + 1):
        for coin in coins:
            if coin <= i:
                dp[i] = min(dp[i], dp[i - coin] + 1)
    return dp[amount] if dp[amount] != float('inf') else -1

139 · 单词拆分(Medium)

思路dp[i] 表示 s[:i] 能否拆分。

python
def wordBreak(s, wordDict):
    word_set = set(wordDict)
    dp = [False] * (len(s) + 1)
    dp[0] = True
    for i in range(1, len(s) + 1):
        for j in range(i):
            if dp[j] and s[j:i] in word_set:
                dp[i] = True
                break
    return dp[len(s)]

300 · 最长递增子序列(Medium)

O(n²) DP

python
def lengthOfLIS(nums):
    if not nums: return 0
    dp = [1] * len(nums)
    for i in range(1, len(nums)):
        for j in range(i):
            if nums[j] < nums[i]:
                dp[i] = max(dp[i], dp[j] + 1)
    return max(dp)

O(n log n) 贪心 + 二分

python
from bisect import bisect_left
def lengthOfLIS(nums):
    tails = []
    for num in nums:
        pos = bisect_left(tails, num)
        if pos == len(tails): tails.append(num)
        else: tails[pos] = num
    return len(tails)

152 · 乘积最大子数组(Medium)

思路:维护最大和最小(负负得正)。

python
def maxProduct(nums):
    ans = cur_max = cur_min = nums[0]
    for num in nums[1:]:
        candidates = (num, cur_max * num, cur_min * num)
        cur_max = max(candidates)
        cur_min = min(candidates)
        ans = max(ans, cur_max)
    return ans

416 · 分割等和子集(Medium)

思路:0-1 背包。详见 背包专题

32 · 最长有效括号(Hard)

思路:DP 或栈。

python
def longestValidParentheses(s):
    stack = [-1]
    ans = 0
    for i, c in enumerate(s):
        if c == '(':
            stack.append(i)
        else:
            stack.pop()
            if not stack:
                stack.append(i)
            else:
                ans = max(ans, i - stack[-1])
    return ans

多维 DP

62 · 不同路径(Medium)

python
def uniquePaths(m, n):
    dp = [[1] * n for _ in range(m)]
    for i in range(1, m):
        for j in range(1, n):
            dp[i][j] = dp[i-1][j] + dp[i][j-1]
    return dp[m-1][n-1]

可压缩到 O(n) 空间。

64 · 最小路径和(Medium)

python
def minPathSum(grid):
    m, n = len(grid), len(grid[0])
    dp = [[0] * n for _ in range(m)]
    dp[0][0] = grid[0][0]
    for j in range(1, n): dp[0][j] = dp[0][j-1] + grid[0][j]
    for i in range(1, m):
        dp[i][0] = dp[i-1][0] + grid[i][0]
        for j in range(1, n):
            dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
    return dp[m-1][n-1]

5 · 最长回文子串(Medium)

思路:中心扩展。

python
def longestPalindrome(s):
    def expand(l, r):
        while l >= 0 and r < len(s) and s[l] == s[r]:
            l -= 1; r += 1
        return l + 1, r - 1
    start = end = 0
    for i in range(len(s)):
        l1, r1 = expand(i, i)
        l2, r2 = expand(i, i + 1)
        if r1 - l1 > end - start: start, end = l1, r1
        if r2 - l2 > end - start: start, end = l2, r2
    return s[start:end+1]

1143 · 最长公共子序列(Medium)

详见 动态规划

72 · 编辑距离(Medium)

详见 动态规划

123 · 买卖股票 III(Hard)

思路:三维 DP(天数、交易次数、持有状态)。详见 股票系列


技巧

136 · 只出现一次的数字(Easy)

思路:异或。a ^ a = 0a ^ 0 = a

python
def singleNumber(nums):
    ans = 0
    for num in nums: ans ^= num
    return ans

169 · 多数元素(Easy)

思路:Boyer-Moore 投票。

python
def majorityElement(nums):
    candidate, count = None, 0
    for num in nums:
        if count == 0: candidate = num
        count += 1 if num == candidate else -1
    return candidate

75 · 颜色分类(Medium)

思路:三指针(荷兰国旗)。

python
def sortColors(nums):
    l, r = 0, len(nums) - 1
    i = 0
    while i <= r:
        if nums[i] == 0:
            nums[l], nums[i] = nums[i], nums[l]
            l += 1; i += 1
        elif nums[i] == 2:
            nums[r], nums[i] = nums[i], nums[r]
            r -= 1
        else:
            i += 1

31 · 下一个排列(Medium)

思路:从右找第一个降序转折点,交换。

287 · 寻找重复数(Medium)

思路:Floyd 判圈(把 nums 视为链表)。

python
def findDuplicate(nums):
    slow = fast = 0
    while True:
        slow = nums[slow]
        fast = nums[nums[fast]]
        if slow == fast: break
    slow = 0
    while slow != fast:
        slow = nums[slow]
        fast = nums[fast]
    return slow

时间 O(n),空间 O(1)。


速查表

按 LeetCode 号索引

#题名难度关键算法时间空间
1两数之和Easy哈希O(n)O(n)
3无重复最长子串Medium滑动窗口O(n)O(字符集)
4两正序数组中位数Hard二分O(log(min(m,n)))O(1)
5最长回文子串Medium中心扩展O(n²)O(1)
11盛最多水的容器Medium双指针O(n)O(1)
15三数之和Medium排序+双指针O(n²)O(1)
20有效括号EasyO(n)O(n)
21合并两个有序链表Easy双指针O(n+m)O(1)
22括号生成Medium回溯O(4ⁿ/√n)O(n)
32最长有效括号Hard栈 / DPO(n)O(n)
33搜索旋转数组Medium二分O(log n)O(1)
34查找范围Medium二分O(log n)O(1)
42接雨水Hard双指针O(n)O(1)
46全排列Medium回溯O(n·n!)O(n)
53最大子数组和MediumDP / 贪心O(n)O(1)
55跳跃游戏Medium贪心O(n)O(1)
56合并区间Medium排序O(n log n)O(1)
62不同路径MediumDPO(mn)O(n)
70爬楼梯EasyDPO(n)O(1)
72编辑距离MediumDPO(mn)O(mn)
75颜色分类Medium三指针O(n)O(1)
76最小覆盖子串Hard滑动窗口O(s
78子集Medium回溯O(2ⁿ·n)O(n)
84柱状图最大矩形Hard单调栈O(n)O(n)
94中序遍历Easy栈 / 递归O(n)O(n)
101对称二叉树EasyDFSO(n)O(h)
104最大深度EasyDFSO(n)O(h)
121买卖股票 IEasy贪心O(n)O(1)
124二叉树最大路径和HardDFSO(n)O(h)
136只出现一次数字Easy异或O(n)O(1)
139单词拆分MediumDPO(n²·k)O(n)
141环形链表Easy快慢指针O(n)O(1)
146LRU 缓存Medium哈希+双向链表O(1)O(n)
148排序链表Hard归并O(n log n)O(log n)
152乘积最大子数组MediumDPO(n)O(1)
169多数元素Easy投票O(n)O(1)
198打家劫舍MediumDPO(n)O(1)
200岛屿数量MediumDFS/BFSO(mn)O(mn)
206反转链表Easy指针O(n)O(1)
207课程表Medium拓扑排序O(V+E)O(V+E)
215第 K 大Medium堆 / 快选O(n) 平均O(k)
234回文链表Easy快慢+翻转O(n)O(1)
236最近公共祖先MediumDFSO(n)O(h)
238除自身以外数组的乘积Medium前缀积O(n)O(1)
239滑动窗口最大值Hard单调队列O(n)O(k)
283移动零Easy双指针O(n)O(1)
287寻找重复数MediumFloyd 判圈O(n)O(1)
295数据流中位数Hard双堆O(log n)O(n)
300LISMediumDP/贪心+二分O(n log n)O(n)
322零钱兑换Medium完全背包O(an)O(a)
416分割等和子集Medium0-1 背包O(n·sum)O(sum)
560和为 K 的子数组Medium前缀和+哈希O(n)O(n)
739每日温度Medium单调栈O(n)O(n)
1143LCSMediumDPO(mn)O(mn)

按算法分类

算法题号
哈希表1, 49, 128, 560, 437
双指针11, 15, 42, 75, 141, 142, 160, 283
滑动窗口3, 76, 209, 239, 438
二分查找4, 33, 34, 35, 74, 153, 300
排序56, 75, 148, 215, 912
20, 84, 155, 394, 739
215, 295, 347, 23
回溯17, 22, 39, 46, 51, 78, 79, 131
贪心55, 121, 122, 123, 188, 309, 714, 763
94, 98, 101, 102, 104, 105, 108, 114, 199, 226, 230, 236, 437, 543, 124
200, 207, 210, 994, 208
DP 一维70, 72, 198, 279, 322, 416, 746
DP 二维5, 62, 64, 118, 152, 300, 1143
DP 区间5, 32, 312
位运算136, 169, 287

刷题节奏建议

入门阶段(前 30 题)

按以下顺序,建立基础:

  1. 哈希:1, 49, 128
  2. 双指针:283, 11, 15
  3. 滑动窗口:3, 438
  4. 二叉树:104, 226, 101, 543
  5. 链表:206, 21, 160
  6. DP 入门:70, 198, 53
  7. 栈/堆:20, 155, 215

巩固阶段(30-70 题)

加入更复杂的题型:

  1. BFS/DFS:200, 994, 79
  2. 回溯:46, 78, 22
  3. 二分:35, 34, 33
  4. DP 进阶:300, 322, 416, 72
  5. 图论:207, 208

突破阶段(70-100 题)

攻克 Hard:

  1. 接雨水:42
  2. 滑动窗口最大值:239
  3. 最小覆盖子串:76
  4. 柱状图最大矩形:84
  5. LRU 缓存:146
  6. 合并 K 个链表:23
  7. 数据流中位数:295
  8. N 皇后:51

延伸阅读

基于 VitePress 构建 · 部署于 Cloudflare Pages