概述
算法策略是解决问题的思路与方法论。面对不同类型的问题,需要选择合适的策略来设计高效的解决方案。
本章节涵盖两大类内容:
| 类别 |
内容 |
核心思想 |
| 基础算法 |
搜索、排序 |
遍历与比较 |
| 算法思想 |
递归、分治、动态规划、贪心、回溯 |
分解问题、状态转移、最优选择 |
一、搜索算法
1.1 搜索的基本思路
搜索的本质是在数据结构中定位目标元素,主要有两种思路:
遍历查找:逐个检查数据结构中的每个元素,直到找到目标或遍历完毕。
- 适用于:无序数据、无索引的线性结构
- 复杂度:O(n)
利用先验信息:借助数据组织结构或预处理的索引实现高效查找。
| 方法 |
预处理 |
查询复杂度 |
适用条件 |
| 二分查找 |
排序 |
O(log n) |
有序数组 |
| 哈希查找 |
建立哈希表 |
O(1) |
键值对映射 |
| 二叉搜索树 |
构建 BST |
O(log n) |
有序键值 |
注意:预处理(如排序、构建索引)本身也需要时间和空间成本,需要权衡使用。
1.2 哈希优化
哈希查找是优化线性查找的常用手段。通过将数据预先存入哈希表,可以将查找复杂度从 O(n) 降低到 O(1)。
典型应用:两数之和
1
2
3
4
5
6
7
8
9
|
def two_sum(nums, target):
"""哈希表优化:O(n) 时间复杂度"""
seen = {}
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
return [seen[complement], i]
seen[num] = i
return []
|
二、排序算法
- 帅地的排序漫画:https://www.iamshuaidi.com/540.html
2.1 排序算法概述
排序评价维度:
- 时间效率:最好、最坏、平均时间复杂度
- 空间复杂度:是否原地排序
- 稳定性:相等元素的相对位置是否保持不变
稳定性示例:
1
2
3
|
原数组:[4, 5, 3₁, 3₂, 2, 9, 0]
稳定排序结果:[0, 2, 3₁, 3₂, 4, 5, 9] ✓ 两个 3 的顺序保持不变
不稳定排序结果:[0, 2, 3₂, 3₁, 4, 5, 9] ✗ 两个 3 的顺序可能改变
|
2.2 简单排序算法
选择排序
每轮从未排序区间选择最小元素,放到已排序区间末尾。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
def selection_sort(nums: list[int]):
n = len(nums)
# 外循环:未排序区间为 [i, n-1]
for i in range(n - 1):
# 内循环:找到未排序区间内的最小元素
min_index = i
for j in range(i + 1, n):
if nums[j] < nums[min_index]:
min_index = j # 记录最小元素的索引
# 将该最小元素与未排序区间的首个元素交换
nums[i], nums[min_index] = nums[min_index], nums[i]
alist = [54, 226, 93, 17, 77, 31, 44, 55, 20]
selection_sort(alist)
print(alist)
|
时间复杂度为 $O(n^2)$
空间复杂度为 $O(1)$,是就地排序
排序不稳定,相同元素的相对位置可能改变
冒泡排序
相邻元素比较交换,大的元素逐步"冒泡"到右侧。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
def bubble_sort(nums: list[int]):
n = len(nums)
# 外循环:未排序区间为 [0, i]
for i in range(n - 1, 0, -1):
swapped = False
# 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for j in range(i):
if nums[j] > nums[j + 1]:
# 交换 nums[j] 与 nums[j + 1]
nums[j], nums[j + 1] = nums[j + 1], nums[j]
swapped = True
if not swapped: # 提前终止优化
break
alist = [54, 226, 93, 17, 77, 31, 44, 55, 20]
bubble_sort(alist)
print(alist)
|
时间复杂度为 $O(n^2)$
空间复杂度为 $O(1)$,是就地排序
排序稳定
插入排序
将每个元素插入到已排序序列的正确位置,类似整理扑克牌。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
def insertion_sort(nums: list[int]):
# 从第二个元素位置开始,每轮循环结束,前 i 个元素都是排好序的
for i in range(1, len(nums)):
base = nums[i] # 相当于右手上待找到正确位置的牌
j = i - 1
# 往前查找位置
while j >= 0 and nums[j] > base:
nums[j + 1] = nums[j] # 将 nums[j] 向右移动一位
j -= 1
nums[j + 1] = base # 将 base 赋值到正确位置
alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
insertion_sort(alist)
print(alist)
|
时间复杂度为 $O(n^2)$
空间复杂度为 $O(1)$,是就地排序
排序稳定
许多编程语言的内置排序函数大致思路:对于长数组,采用基于分治策略的排序算法,例如快速排序;对于短数组,直接使用插入排序
原因是快速排序这类算法基于分治策略,往往包含更多单元计算操作。而在数据量较小时,$n^2$ 和 $nlogn$ 的数值比较接近,复杂度不占主导地位,每轮中的单元操作数量起到决定性作用
希尔排序
插入排序的改进版,通过设置递减的**步长(gap)**进行分组插入。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
def shell_sort(nums: list[int]):
n = len(nums)
# 将步长初始化为列表长度整除 2
gap = n // 2
while gap > 0:
for i in range(gap, n):
j = i
# 根据条件交换元素
# 确保相距一个步长的两个元素是前小后大
while j >= gap and nums[j - gap] > nums[j]:
nums[j - gap], nums[j] = nums[j], nums[j - gap]
j -= gap
# j 减去一个 gap 再进入 while 循环
# 因为列表长度很长,可能会有好几个的 gap
# 缩小步长,直到最大步长等于 0 为止
gap = gap // 2
nums = [54, 26, 93, 17, 77, 31, 44, 55, 20]
shell_sort(nums)
print(nums)
|
特点:时间复杂度与步长选择相关,平均 O(n^1.3),最坏 O(n²),空间复杂度 O(1),不稳定。
2.3 高效排序算法
快速排序
数组中的第 k 个最大元素:https://leetcode.cn/problems/kth-largest-element-in-an-array/
快速排序的核心操作是 哨兵划分
选择数组中的某个元素作为“基准数”,将所有小于基准数的元素移到其左侧,而大于基准数的元素移到其右侧
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
|
def partition(nums: list[int], left: int, right: int) -> int:
"""哨兵划分"""
# 以 nums[left] 为基准数
i, j = left, right
while i < j:
while i < j and nums[j] >= nums[left]:
j -= 1 # 从右向左找首个小于基准数的元素
while i < j and nums[i] <= nums[left]:
i += 1 # 从左向右找首个大于基准数的元素
# 元素交换
nums[i], nums[j] = nums[j], nums[i]
# 将基准数交换至两子数组的分界线
nums[i], nums[left] = nums[left], nums[i]
return i # 返回基准数的索引
def quick_sort(nums: list[int], left: int, right: int):
"""快速排序"""
# 子数组长度为 1 时终止递归
if left >= right:
return
# 哨兵划分
pivot = partition(nums, left, right)
# 递归左子数组、右子数组
quick_sort(nums, left, pivot - 1)
quick_sort(nums, pivot + 1, right)
nums = [54, 26, 93, 17, 77, 31, 44, 55, 20]
quick_sort(nums, 0, len(nums) - 1)
print(nums)
|
特点:平均/最优 O(n log n),最坏 O(n²)(数据有序时),空间复杂度 O(log n),不稳定。
优化技巧:随机选择基准元素,避免最坏情况发生。
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
class mysort():
def set_random_pivot(self, nums, low, high):
"""
随机选择基准值
再将基准值与数组的第一个元素互换
"""
pivot = random.randint(low, high)
nums[low], nums[pivot] = nums[pivot], nums[low]
return nums
def partition(self, nums, low, high):
"""
快速排序核心内容
找到基准值的“正确”位置
"""
# 取出基准值
pivot = nums[low]
# 设置左右指针
left, right = low, high
while left < right:
# 先让 right 指针向左移动寻找小于基准值的位置
while left<right and nums[right]>=pivot: #注意这里的右指针是大于等于基准值时均可向左移动
right -= 1
nums[left] = nums[right]
# 再让 left 指针向右移动寻找大于基准值的位置
while left<right and nums[left]<=pivot:
left += 1
nums[right] = nums[left]
# 之后将基准值放在 left 指向的位置
nums[left] = pivot
# 返回安放好的基准值的位置 left
return left
def quickly_sort(self, nums, low, high):
"""
递归完成排序
"""
if low >= high: #注意这个停止条件,low 需要与 high 重合或者超过它
return
# nums = self.set_random_pivot(nums, low, high) #随机选择基准值【可选】
ind = self.partition(nums, low, high)
self.quickly_sort(nums, low, ind-1) #ind左侧排序
self.quickly_sort(nums, ind+1, high) #ind右侧排序
return nums
nums = [3,2,1,5,6,4]
nums = [3,2,3,1,2,4,5,5,6]
mysort().quickly_sort(nums, 0, len(nums)-1)
|
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
41
|
def quick_sort(alist, start, end):
# 递归的退出条件
if start >= end:
return
# 设定起始元素为要寻找位置的基准元素
mid = alist[start]
# low 为序列左边的【由左向右移动的游标】
low = start
# high 为序列右边的【由右向左移动的游标】
high = end
while low < high:
# 如果游标未重合
# high 游标向左寻找小于基准元素的元素
while low < high and alist[high] >= mid:
high -= 1
# 游标重合或者找到了元素
alist[low] = alist[high]
# 如果游标未重合
# low 游标向右寻找大于基准元素的元素
while low < high and alist[low] <= mid:
low += 1
# 游标重合或者找到了元素
alist[high] = alist[low]
# 退出循环后,low 与 high 游标重合
# 此时两游标所指位置为【基准元素的正确位置】
alist[low] = mid
# 对基准元素左边的子序列进行快速排序
quick_sort(alist, start, low-1)
# 对基准元素右边的子序列进行快速排序
quick_sort(alist, low+1, end)
alist = [54,26,93,17,77,31,44,55,20]
quick_sort(alist,0,len(alist)-1)
print(alist)
|
归并排序
分治策略的典型应用:先递归拆分,再合并有序子数组。
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
|
# 归并排序
def merge_sort(alist):
# 递归的退出条件
if len(alist) <= 1:
return alist
# 二分分解
num = len(alist)//2
# 递归操作
left = merge_sort(alist[:num]) # 不包含 num
right = merge_sort(alist[num:]) # 包含 num
# 合并
return merge(left, right)
def merge(left, right):
# 将两个【有序数组】left[] 和 right[] 合并成一个大的有序数组
# 用于遍历两个数组的下标指针
l, r = 0, 0
result = []
while l<len(left) and r<len(right):
# 两个数组若其中有一个遍历完,就跳出 while 循环
if left[l] < right[r]:
result.append(left[l])
l += 1
else:
result.append(right[r])
r += 1
result += left[l:]
result += right[r:]
# 特殊情况是两个数组长度相等,那么上述两句就不起作用
return result
alist = [54,26,93,17,77,31,44,55,20]
sorted_alist = merge_sort(alist)
print(sorted_alist)
|
特点:最优/平均/最坏都是 O(n log n),空间复杂度 O(n)(需要额外数组),稳定。
归并排序适用于需要稳定排序或外部排序(数据量大于内存)的场景。
堆排序
利用堆这种数据结构实现排序。
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
|
def heap_sort(nums):
n = len(nums)
def sift_down(start, end):
root = start
while True:
child = 2 * root + 1
if child > end:
break
if child + 1 <= end and nums[child] < nums[child + 1]:
child += 1
if nums[root] < nums[child]:
nums[root], nums[child] = nums[child], nums[root]
root = child
else:
break
# 建堆:从最后一个非叶子节点下沉
for i in range(n // 2 - 1, -1, -1):
sift_down(i, n - 1)
# 逐个取出堆顶
for i in range(n - 1, 0, -1):
nums[0], nums[i] = nums[i], nums[0]
sift_down(0, i - 1)
|
特点:最优/平均/最坏都是 O(n log n),空间复杂度 O(1),不稳定。
采用 priority queue 或者说在 python 中的 heapq 求 top k 采用 最小堆(默认)
采用 最大堆 的时候可以采用 push 负的 value
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
41
42
43
44
45
|
import heapq
def heap_operations():
heap = [4, 2, 1, 3]
# heapify
heapq.heapify(heap)
# top
top = heap[0]
# heappop
top = heapq.heappop(heap)
# heappush
heapq.heappush(heap, 5)
# heappushpop = push + pop
heapq.heappushpop(heap, 0)
# heapreplace = pop + push
heapq.heapreplace(heap, 0)
data = [10, 5, 18, 2, 37, 3, 8, 7, 19, 1]
heapq.heapify(data)
old, new = 8, 22 # increase the 8 to 22
i = data.index(old)
data[i] = new
# _siftup, from root to leaf, when increase
heapq._siftup(data, i)
old, new = 10, 4 # decrease the 10 to 4
i = data.index(old)
data[i] = new
# _siftdown, from leaf to root, when decrease
heapq._siftdown(data, 0, i)
# find n largest by queue 从队列中找出前 n 个最大的
heapq.nlargest(data, 3)
# find n smallest by queue
heapq.nsmallest(data, 3)
# Merge multiple sorted inputs into a single sorted output
# e.g. merge timestamped entries from multiple log files
heapq.merge([1, 3, 5, 7], [0, 2, 4, 8], [5, 10, 15, 20], [], [25])
|
2.4 非比较排序
非比较排序不通过元素比较确定顺序,通常可达到线性时间复杂度。
计数排序
适用于范围较小的整数排序。
1
2
3
4
5
6
7
8
9
10
11
|
def counting_sort(nums, max_val):
"""计数排序"""
count = [0] * (max_val + 1)
for num in nums:
count[num] += 1
sorted_idx = 0
for val, freq in enumerate(count):
for _ in range(freq):
nums[sorted_idx] = val
sorted_idx += 1
|
特点:时间复杂度 O(n + k),空间复杂度 O(k),稳定(需要额外处理)。
适用于:整数、范围已知、数据分布均匀的场景。
桶排序
将数据分到有限数量的桶中,每个桶内单独排序。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
def bucket_sort(nums, bucket_size=5):
if not nums:
return nums
# 确定数据范围
min_val, max_val = min(nums), max(nums)
bucket_count = (max_val - min_val) // bucket_size + 1
buckets = [[] for _ in range(bucket_count)]
# 分配到各桶
for num in nums:
buckets[(num - min_val) // bucket_size].append(num)
# 各桶内排序并合并
result = []
for bucket in buckets:
bucket.sort()
result.extend(bucket)
return result
|
特点:平均时间复杂度 O(n + k),最坏 O(n² log n),空间复杂度 O(n + k)。
适用于:数据分布均匀、浮点数排序。
基数排序
按位数逐个排序,从低位到高位(或相反)。
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
|
def radix_sort(nums):
"""基数排序(按最低位优先)"""
max_val = max(nums)
exp = 1
while max_val // exp > 0:
counting_sort_by_exp(nums, exp)
exp *= 10
def counting_sort_by_exp(nums, exp):
n = len(nums)
output = [0] * n
count = [0] * 10
for num in nums:
digit = (num // exp) % 10
count[digit] += 1
for i in range(1, 10):
count[i] += count[i - 1]
for i in range(n - 1, -1, -1):
digit = (nums[i] // exp) % 10
output[count[digit] - 1] = nums[i]
count[digit] -= 1
for i in range(n):
nums[i] = output[i]
|
特点:时间复杂度 O(d × (n + k)),其中 d 是位数,稳定。
适用于:整数、字符串排序。
2.5 排序算法综合对比
| 排序算法 |
平均时间复杂度 |
最坏时间复杂度 |
最优时间复杂度 |
空间复杂度 |
稳定性 |
| 冒泡排序 |
O(n²) |
O(n²) |
O(n) |
O(1) |
稳定 |
| 选择排序 |
O(n²) |
O(n²) |
O(n²) |
O(1) |
不稳定 |
| 插入排序 |
O(n²) |
O(n²) |
O(n) |
O(1) |
稳定 |
| 希尔排序 |
O(n^1.3) |
O(n²) |
O(n log n) |
O(1) |
不稳定 |
| 快速排序 |
O(n log n) |
O(n²) |
O(n log n) |
O(log n) |
不稳定 |
| 归并排序 |
O(n log n) |
O(n log n) |
O(n log n) |
O(n) |
稳定 |
| 堆排序 |
O(n log n) |
O(n log n) |
O(n log n) |
O(1) |
不稳定 |
| 计数排序 |
O(n + k) |
O(n + k) |
O(n + k) |
O(k) |
稳定 |
| 桶排序 |
O(n + k) |
O(n²) |
O(n + k) |
O(n + k) |
稳定 |
| 基数排序 |
O(d × n) |
O(d × n) |
O(d × n) |
O(n + k) |
稳定 |
注:k 为数据范围,d 为位数。
三、算法设计思想
3.1 递归
递归通过函数调用自身来解决问题,包含两个阶段:
- 递:不断深入调用,传入更小/更简化的参数
- 归:满足终止条件后,逐层返回结果
递归函数每次调用自身时,系统都会为新开启的函数分配内存,以存储局部变量、调用地址和其他信息等。这将导致两方面的结果。
函数的上下文数据都存储在称为“栈帧空间”的内存区域中,直至函数返回后才会被释放。因此,递归通常比迭代更加耗费内存空间。
递归调用函数会产生额外的开销。因此递归通常比循环的时间效率更低。
在实际中,编程语言允许的递归深度通常是有限的,过深的递归可能导致栈溢出错误。
1
2
3
4
5
|
# 计算阶乘
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
|
递归的特点:
| 方面 |
说明 |
| 内存消耗 |
每次调用占用栈帧空间,递归层级过深可能导致栈溢出 |
| 时间开销 |
函数调用有额外开销,通常比循环效率低 |
| 适用场景 |
问题可分解为相似子问题(如树的遍历、分治算法) |
尾递归优化:当递归调用是函数的最后一个操作时,可进行尾递归优化,避免栈帧累积。但 Python 默认不支持此优化。
1
2
3
4
5
|
def tail_factorial(n, acc=1):
"""尾递归形式"""
if n <= 1:
return acc
return tail_factorial(n - 1, acc * n)
|
3.2 分治法(Divide and Conquer)
分治法的核心思想:
- 分解(Divide):将问题划分为若干规模较小的子问题
- 解决(Conquer):递归解决子问题
- 合并(Combine):将子问题的解合并为原问题的解
典型应用:
| 算法 |
分解策略 |
合并策略 |
| 归并排序 |
对半分 |
合并两个有序数组 |
| 快速排序 |
按基准划分 |
无需显式合并(原地排序) |
| 二分查找 |
取中点缩小范围 |
无需合并 |
3.3 动态规划(Dynamic Programming)
动态规划适用于重叠子问题和最优子结构的问题,通过保存子问题结果避免重复计算。
解题四步法:
- 定义状态:确定 dp 数组及其含义
- 状态转移方程:建立子问题之间的关系
- 初始化条件:确定边界情况
- 遍历顺序:确定如何填表
经典问题示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# 斐波那契数列(带备忘录的递归)
def fib(n, memo=None):
if memo is None:
memo = {}
if n in memo:
return memo[n]
if n <= 1:
return n
memo[n] = fib(n - 1, memo) + fib(n - 2, memo)
return memo[n]
# 爬楼梯(自底向上)
def climb_stairs(n):
if n <= 2:
return n
dp = [0] * (n + 1)
dp[1], dp[2] = 1, 2
for i in range(3, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
|
动态规划分类:
| 类型 |
特点 |
经典问题 |
| 背包问题 |
选或不选、容量限制 |
01 背包、完全背包 |
| 序列 DP |
一维/二维数组 |
最长递增子序列 |
| 区间 DP |
区间合并代价 |
合并石子、戳气球 |
| 状态压缩 DP |
用位运算表示状态 |
旅行商问题 |
3.4 贪心算法(Greedy)
贪心算法在每一步都选择当前最优的解,期望获得全局最优。
贪心 vs 动态规划:
- 贪心:每步最优 → 全局可能最优(需证明)
- 动态规划:保存所有子问题的解 → 保证全局最优
适用条件:问题具有贪心选择性质和最优子结构。
典型问题:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# 区间调度问题:选择最多不重叠的区间
def interval_schedule(intervals):
intervals.sort(key=lambda x: x[1]) # 按结束时间排序
count, current_end = 0, 0
for start, end in intervals:
if start >= current_end:
count += 1
current_end = end
return count
# 钱币找零:尽可能使用大面额
def coin_change(coins, amount):
coins.sort(reverse=True)
count = 0
for coin in coins:
count += amount // coin
amount %= coin
return count
|
贪心算法典例:
| 问题 |
贪心策略 |
链接 |
| 分发糖果 |
两次遍历,左右规则 |
LC 135 |
| 盛最多水的容器 |
双指针向中间靠拢 |
LC 11 |
| 跳跃游戏 |
维护最远可达位置 |
LC 55 |
3.5 回溯算法(Backtracking)
回溯算法通过枚举所有可能的解,逐步构造解空间,并剪枝掉不满足条件的分支。
回溯的本质:在解空间树上进行深度优先搜索。
模板:
1
2
3
4
5
6
7
8
9
10
|
def backtrack(nums, path, result):
if 满足终止条件:
result.append(path[:]) # 记录解
return
for choice in 可选元素:
if 满足剪枝条件:
path.append(choice) # 做选择
backtrack(nums, path, result) # 递归
path.pop() # 撤销选择
|
回溯算法典例:
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
|
# 全排列
def permute(nums):
result = []
def backtrack(path, used):
if len(path) == len(nums):
result.append(path[:])
return
for i, num in enumerate(nums):
if not used[i]:
used[i] = True
path.append(num)
backtrack(path, used)
path.pop()
used[i] = False
backtrack([], [False] * len(nums))
return result
# N 皇后
def solve_n_queens(n):
result = []
def backtrack(row, cols, diag1, diag2, board):
if row == n:
result.append([''.join(row) for row in board])
return
for col in range(n):
d1, d2 = row - col, row + col
if col in cols or d1 in diag1 or d2 in diag2:
continue
board[row][col] = 'Q'
backtrack(row + 1, cols | {col}, diag1 | {d1}, diag2 | {d2}, board)
board[row][col] = '.'
backtrack(0, set(), set(), set(), [['.' for _ in range(n)] for _ in range(n)])
return result
|
回溯算法典例:
| 问题 |
说明 |
链接 |
| 全排列 |
生成数组所有排列 |
LC 46 |
| N 皇后 |
在 N×N 棋盘放 N 个皇后 |
LC 51 |
| 子集 |
生成集合的所有子集 |
LC 78 |
| 组合总和 |
目标和的组合 |
LC 39 |
拓展与后续学习建议
-
深入进阶方向
- 学习分支限界法(Branch and Bound),与回溯类似但采用广度优先搜索 + 剪枝
- 掌握摊销分析方法,理解动态数组扩容、均摊时间复杂度的概念
- 研究排序算法的工程实现(如 Python Timsort、Java Dual-Pivot QuickSort)
-
算法练习建议
- LeetCode Hot 100 中关于动态规划、贪心、回溯的题目
- 重点练习:背包问题、股票买卖问题、字符串 DP
- 学习如何识别问题类型:看到最值/最优 → 贪心或 DP;看到全部解 → 回溯
-
面试高频考点
- 手写快速排序、归并排序
- 动态规划:斐波那契、爬楼梯、编辑距离
- 回溯:全排列、N 皇后
- 贪心:区间调度、加油站问题