LQB(4)-python-DFS搜索

前言

DFS 即深度优先搜索(Depth-First Search),是一种用于遍历或搜索树或图的算法,有三种核心的应用场景(基础遍历、回溯、剪枝)。


一、DFS-基础遍历

1. 核心原理

  • 深度优先搜索(DFS) 是一种遍历或搜索树/图的算法,优先沿着一条路径尽可能深入,直到无法继续再回溯。

  • 实现方式

    • 递归:隐式利用系统调用栈。

    • 栈模拟:显式使用栈数据结构。

2. 代码实现

(1) 递归实现(树结构)
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def dfs_recursive(node):
    if not node:
        return
    print(node.val)          # 前序遍历(操作位置可调整)
    dfs_recursive(node.left)
    dfs_recursive(node.right)

# 示例树结构
root = TreeNode(1, TreeNode(2, TreeNode(4), TreeNode(5)), TreeNode(3))
dfs_recursive(root)  # 输出: 1 → 2 → 4 → 5 → 3
(2) 栈模拟实现(图结构)
def dfs_stack(graph, start):
    visited = set()
    stack = [start]
    while stack:
        node = stack.pop()
        if node not in visited:
            visited.add(node)
            print(node)       # 处理当前节点
            # 将未访问的邻居逆序压入栈(保证顺序)
            for neighbor in reversed(graph[node]):
                if neighbor not in visited:
                    stack.append(neighbor)

# 示例图结构
graph = {
    'A': ['B', 'C'],
    'B': ['D', 'E'],
    'C': ['F'],
    'D': [],
    'E': ['F'],
    'F': []
}
dfs_stack(graph, 'A')  # 输出: A → C → F → B → E → D

3. 关键知识点

  • 遍历顺序:前序、中序、后序遍历(树结构)。

  • 避免重复访问:使用 visited 集合记录已访问节点(图结构)。

  • 应用场景:路径存在性判断、拓扑排序、连通分量分析。


二、DFS-回溯(Backtracking)

1. 核心原理

  • 回溯法 是 DFS 的一种扩展,用于求解 组合、排列、子集 等需要穷举所有可能解的问题。

  • 核心思想:通过递归尝试所有可能的选择,并在发现当前路径无法得到解时撤销(回溯)最后一步选择。

2. 代码实现(全排列问题)

def permute(nums):
    def backtrack(path, used):
        if len(path) == len(nums):
            res.append(path.copy())  # 记录有效解
            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

    res = []
    backtrack([], [False]*len(nums))
    return res

print(permute([1, 2, 3]))
# 输出: [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]

3. 关键知识点

  • 状态管理:通过 path 记录当前路径,used 记录已选元素。

  • 撤销操作:递归返回后必须撤销最后一步选择(path.pop())。

  • 剪枝优化:通过条件提前终止无效分支(见下文剪枝部分)。

  • 应用场景:N 皇后、数独、组合求和、子集问题。


三、DFS-剪枝(Pruning)

1. 核心原理

  • 剪枝 是在回溯过程中,通过提前判断某些分支不可能得到有效解,直接跳过这些分支以提升效率。

  • 常见剪枝策略

    • 去重剪枝:跳过重复选择(需先排序)。

    • 约束剪枝:根据问题条件提前终止无效路径。

2. 代码实现(组合总和,含剪枝)

def combination_sum(candidates, target):
    def backtrack(start, path, remain):
        if remain == 0:
            res.append(path.copy())
            return
        for i in range(start, len(candidates)):
            # 剪枝1:跳过重复组合(需先排序)
            if i > start and candidates[i] == candidates[i-1]:
                continue
            # 剪枝2:提前终止无效分支
            if candidates[i] > remain:
                break
            path.append(candidates[i])
            backtrack(i+1, path, remain - candidates[i])  # 不可重复选同一元素
            path.pop()

    candidates.sort()  # 排序以便剪枝
    res = []
    backtrack(0, [], target)
    return res

print(combination_sum([10, 1, 2, 7, 6, 1, 5], 8))
# 输出: [[1, 1, 6], [1, 2, 5], [1, 7], [2, 6]]

3. 关键知识点

  • 排序预处理:方便去重剪枝(如 [1,2,1] 排序后为 [1,1,2])。

  • 剪枝条件设计

    • 去重剪枝i > start and candidates[i] == candidates[i-1]

    • 约束剪枝candidates[i] > remain

  • 应用场景:组合总和、子集 II、全排列 II 等含重复元素的问题。


四、总结对比

类型 特点 核心操作 应用场景
基础DFS 单纯遍历所有节点 递归/栈,无状态回溯 路径存在性判断、连通性分析
回溯DFS 穷举所有可能解 状态记录与撤销(append/pop 组合、排列、子集问题
剪枝优化 提前终止无效分支 条件判断跳过分支 含重复元素或约束的优化问题

学习建议

  1. 从基础 DFS 理解递归和栈的工作原理。

  2. 通过回溯模板掌握状态管理(选择与撤销)。

  3. 结合具体问题设计剪枝条件,减少无效计算。

练习题

1.LITS 游戏 - 蓝桥云课

5.分糖果 - 蓝桥云课

你可能感兴趣的:(蓝桥杯python组,深度优先,算法,python,蓝桥杯)