基础算法:深度优先搜索(DFS)和广度优先搜索(BFS)

441 阅读4分钟

深度优先搜索(DFS)和广度优先搜索(BFS)

深度优先搜索(DFS)广度优先搜索(BFS) 是两种常见的图(或树)遍历算法,它们用于探索图或树中的所有节点。两者的区别主要在于遍历的顺序和使用的数据结构。

1. 深度优先搜索(DFS)

定义

  • 深度优先搜索(DFS)是一种沿着树的深度遍历图的算法。它尽可能深入每一条分支,当节点的所有子节点都被访问过后,再回溯到父节点继续搜索。
  • DFS 使用递归或栈来实现,通常会先访问当前节点的子节点,直到到达叶子节点或没有未访问的节点为止。

实现思路

  1. 从起始节点开始,访问当前节点并标记为已访问。
  2. 递归地访问当前节点的邻接节点(或子节点)。
  3. 如果没有更多未访问的邻接节点,回溯到上一个节点,继续检查其未访问的邻接节点。
  4. 如果所有节点都被访问过,算法结束。

数据结构:使用栈(Stack)或者递归来实现。

时间复杂度

  • 时间复杂度:O(V + E),其中 V 是图中的节点数,E 是边的数目。每个节点和每条边都需要访问一次。
  • 空间复杂度:O(V),栈空间或递归调用栈的深度可能会达到图的节点数。

特点

  • DFS 适合搜索路径、拓扑排序、连通分量等问题。
  • 它可能会进入无限循环,特别是在图中存在环时,因此需要避免重复访问。

代码示例(Python):

# 深度优先搜索 DFS
def dfs(graph, start, visited=None):
    if visited is None:
        visited = set()
    visited.add(start)
    print(start, end=' ')
    
    for neighbor in graph[start]:
        if neighbor not in visited:
            dfs(graph, neighbor, visited)

# 示例图(邻接表表示法)
graph = {
    'A': ['B', 'C'],
    'B': ['A', 'D', 'E'],
    'C': ['A', 'F'],
    'D': ['B'],
    'E': ['B', 'F'],
    'F': ['C', 'E']
}

dfs(graph, 'A')  # 从节点 A 开始深度优先搜索

输出

A B D E F C

2. 广度优先搜索(BFS)

定义

  • 广度优先搜索(BFS)是一种从根节点开始,首先访问节点的所有邻接节点,然后逐层向外扩展,访问每一层的节点。
  • BFS 使用队列来存储待访问的节点,每次从队列中取出一个节点进行访问,然后将该节点的所有未访问的邻接节点加入队列。

实现思路

  1. 从起始节点开始,将其加入队列并标记为已访问。
  2. 从队列中取出一个节点,访问该节点并将其所有未访问的邻接节点加入队列。
  3. 重复步骤2,直到队列为空,即所有可访问节点都被遍历过。

数据结构:使用队列(Queue)来实现。

时间复杂度

  • 时间复杂度:O(V + E),每个节点和每条边都需要访问一次。
  • 空间复杂度:O(V),队列可能会存储图中所有的节点。

特点

  • BFS 适用于寻找最短路径(无权图),层级遍历,或者是检查连通性等问题。
  • BFS 会遍历每一层的节点,适用于处理最短路径和最短距离等问题。

代码示例(Python):

# 广度优先搜索 BFS
from collections import deque

def bfs(graph, start):
    visited = set()
    queue = deque([start])  # 初始化队列,加入起始节点
    visited.add(start)
    
    while queue:
        node = queue.popleft()  # 弹出队列的第一个节点
        print(node, end=' ')
        
        for neighbor in graph[node]:
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append(neighbor)  # 将未访问的邻居节点加入队列

# 示例图(邻接表表示法)
graph = {
    'A': ['B', 'C'],
    'B': ['A', 'D', 'E'],
    'C': ['A', 'F'],
    'D': ['B'],
    'E': ['B', 'F'],
    'F': ['C', 'E']
}

bfs(graph, 'A')  # 从节点 A 开始广度优先搜索

输出

A B C D E F

DFS 和 BFS 的区别

特点深度优先搜索(DFS)广度优先搜索(BFS)
遍历方式先深入子节点,直到无法继续,再回溯按层次逐层遍历,先访问同一层的节点
使用的数据结构栈(或递归调用栈)队列
适用场景适用于需要探索路径、拓扑排序等场景适用于求解最短路径、层次遍历等场景
时间复杂度O(V + E)O(V + E)
空间复杂度O(V)O(V)
是否能找到最短路径无法保证能找到最短路径
搜索顺序深度优先,可能先探索一条路径直到终点广度优先,先访问近距离节点

总结:

  • DFS 适合用来查找图中的路径、拓扑排序、检测环、连通性等问题。
  • BFS 适合用来查找最短路径、层次遍历,或处理无权图的最短距离问题。

根据问题的不同,选择适合的搜索算法会使问题的求解更加高效。