Contents

数据结构与算法-4算法策略

概述

算法策略是解决问题的思路与方法论。面对不同类型的问题,需要选择合适的策略来设计高效的解决方案。

本章节涵盖两大类内容:

类别 内容 核心思想
基础算法 搜索、排序 遍历与比较
算法思想 递归、分治、动态规划、贪心、回溯 分解问题、状态转移、最优选择

一、搜索算法

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. 稳定性:相等元素的相对位置是否保持不变

稳定性示例

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),不稳定

  • 前 K 大的数模式 HEAP

采用 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)

分治法的核心思想:

  1. 分解(Divide):将问题划分为若干规模较小的子问题
  2. 解决(Conquer):递归解决子问题
  3. 合并(Combine):将子问题的解合并为原问题的解

典型应用

算法 分解策略 合并策略
归并排序 对半分 合并两个有序数组
快速排序 按基准划分 无需显式合并(原地排序)
二分查找 取中点缩小范围 无需合并

3.3 动态规划(Dynamic Programming)

动态规划适用于重叠子问题最优子结构的问题,通过保存子问题结果避免重复计算。

解题四步法

  1. 定义状态:确定 dp 数组及其含义
  2. 状态转移方程:建立子问题之间的关系
  3. 初始化条件:确定边界情况
  4. 遍历顺序:确定如何填表

经典问题示例

 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

拓展与后续学习建议

  1. 深入进阶方向

    • 学习分支限界法(Branch and Bound),与回溯类似但采用广度优先搜索 + 剪枝
    • 掌握摊销分析方法,理解动态数组扩容、均摊时间复杂度的概念
    • 研究排序算法的工程实现(如 Python Timsort、Java Dual-Pivot QuickSort)
  2. 算法练习建议

    • LeetCode Hot 100 中关于动态规划、贪心、回溯的题目
    • 重点练习:背包问题、股票买卖问题、字符串 DP
    • 学习如何识别问题类型:看到最值/最优 → 贪心或 DP;看到全部解 → 回溯
  3. 面试高频考点

    • 手写快速排序、归并排序
    • 动态规划:斐波那契、爬楼梯、编辑距离
    • 回溯:全排列、N 皇后
    • 贪心:区间调度、加油站问题