图论:图的遍历(DFS vs. BFS)
引言
图的遍历是图论中的核心概念,通过不同的遍历方 法能够有效地处理多种问题,例如连通性检测、路径查找、图的最小生成树等。本篇我们将重点探讨图的深度优先搜索(DFS)和广度优先搜索(BFS)两种常见的图遍历方法,分析它们的原理、应用场景以及实现方法,并通过实际案例代码分析问题解决方案。
基本概念
图是一种表示对象间关系的数据结构,由顶点和边组成。按照边的方向,图可以分为有向图和无向图。图的遍历主要有两种方式:
- 深度优先搜索(DFS):优先深入图的分支,直到无法继续时回溯。
- 广度优先搜索(BFS):优先探索与当前节点相邻的节点,然后逐层向外扩展。
无向图示例
下面是一个无向图示例,适合用于探讨图的遍历(深度优先搜索和广度优先搜索)。
A
/ \
B C
/| |
D | |
\| |
E--F
| |
G H
\ |
I
在这个图中,节点的连接关系如下:
- A 连接 B、C
- B 连接 D、E
- C 连接 E、F
- D 连接 E
- E 连接 G、F
- F 连接 H
- G 连接 I
- H 连接 I
绘制图形
下面的代码使用 networkx
和 matplotlib
库绘制该图。请确保已安装这两个库(如果尚未安装,请运行 pip install networkx matplotlib
)。
以下是绘制上述无向图的 Python 代码:
import matplotlib.pyplot as plt
import networkx as nx
# 创建空图
G = nx.Graph()
# 添加节点和边
edges = [
('A', 'B'),
('A', 'C'),
('B', 'D'),
('B', 'E'),
('C', 'E'),
('C', 'F'),
('D', 'E'),
('E', 'F'),
('E', 'G'),
('F', 'H'),
('G', 'I'),
('H', 'I')
]
G.add_edges_from(edges)
# 绘制图形
pos = nx.spring_layout(G) # 选择布局
nx.draw(G, pos, with_labels=True, node_color='lightblue', node_size=700, font_size=12, font_weight='bold', edge_color='gray')
# 显示图形
plt.title("无向图示例")
plt.show()
运行上述代码将生成一个表示上述无向图的可视化图形,为后续对深度优先搜索(DFS)和广度优先搜索(BFS)的探讨提供一个演示基础。
代码释意
- 导入库:使用
matplotlib.pyplot
来绘制图形,使用networkx
来处理图结构。 - 创建图:创建一个无向图对象
G
。 - 添加边:通过
add_edges_from
方法将边添加到图中,定义节点之间的连接关系。 - 设置图的布局:使用
spring_layout
选择图形的布局,这有助于更清晰地展示图的结构。 - 绘制图形:使用
nx.draw
来绘制图,设置节点的颜色、大小和标签。 - 显示图:通过
plt.show()
来显示绘制的图形。
深度优先搜索(DFS)
基本概念
深度优先搜索利用栈(可以用递归实现)来访问图中的节点。当访问一个节点时,会深度探索该节点的所有相邻节点,直到无法继续。
算法步骤:
- 从起始节点 开始,标记为已访问。
- 递归访问未访问的相邻节点。
- 如果所有相邻节点都已访问,回溯到上一个节点。
下面我们将逐步展示如何使用 Python 代码演示无向图的深度优先搜索(DFS)过程与效果吗,并实现 DFS 算法。
可视化 DFS 过程
在实现 DFS 并打印结果后,我们可以通过 matplotlib
绘制这幅图以可视化 DFS 的遍历过程。为了展示仍然在访问中的节点,我们将在访问每个节点时更新图像。
以下是完整的 DFS 过程可视化实现:
import matplotlib.pyplot as plt
import networkx as nx
import time
class Graph:
def __init__(self):
self.graph = nx.Graph() # 使用 networkx 的 Graph 对象
def add_edge(self, u, v):
self.graph.add_edge(u, v) # 直接添加边到 Graph 对象
def dfs(self, start):
visited = set()
path = []
self._dfs_util(start, visited, path)
return path
def _dfs_util(self, vertex, visited, path):
visited.add(vertex)
path.append(vertex) # 记录路径
self.visualize_dfs(path) # 可视化图遍历
for neighbor in self.graph[vertex]: # 使用 networkx 的方式访问相邻节点
if neighbor not in visited:
self._dfs_util(neighbor, visited, path)
def visualize_dfs(self, path):
plt.clf() # 清空当前的图
pos = nx.spring_layout(self.graph)
nx.draw(self.graph, pos, with_labels=True, node_color='lightblue', node_size=700, font_size=12, font_weight='bold', edge_color='gray')
# 着色已访问的节点
nx.draw_networkx_nodes(self.graph, pos, nodelist=path, node_color='orange')
plt.title("深度优先搜索(DFS)可视化")
plt.pause(0.5) # 暂停以绘制图像
# 创建无向图
G = Graph()
edges = [
('A', 'B'), ('A', 'C'), ('B', 'D'), ('B', 'E'),
('C', 'E'), ('C', 'F'), ('D', 'E'), ('E', 'F'),
('E', 'G'), ('F', 'H'), ('G', 'I'), ('H', 'I')
]
for u, v in edges:
G.add_edge(u, v)
# 设置图的布局
plt.ion() # 开启交互模式
plt.figure(figsize=(8, 6))
# 执行 DFS
dfs_result = G.dfs('A')
print("深度优先搜索的结果:", dfs_result)
plt.ioff() # 关闭交互模式
plt.show() # 显示最终图
# 深度优先搜索的结果: ['A', 'B', 'D', 'E', 'C', 'F', 'H', 'I', 'G']
代码释意
- 图的构建:使用
add_edge
方法构建无向图,确保每个节点都相互连接。 - DFS 实现:使用递归方法进行深度优先搜索,同时在每次访问节点时调用可视化函数。
- 可视化函数:使用
matplotlib
不断更新图的显示,以展示各个节点的遍历状态。 - 交互模式:通过开启交互模式(
plt.ion()
),允许我们逐步更新图。
运行示例
运行上面的完整代码,可以看到图的遍历过程。每次访问一个节点时,节点将会变为橙色,表示已访问。最终,程序也会打印出 DFS 的结果路径。
深度优先搜索的结果: ['A', 'B', 'D', 'E', 'C', 'F', 'H', 'I', 'G']
深度优先搜索(DFS)
下面是 DFS 算法的实现。我们使用一个递归方法进行实现,在访问每个节点时记录路径,以便可视化图遍历结果。
import matplotlib.pyplot as plt
import networkx as nx
class Graph:
def __init__(self):
self.graph = {}
def add_edge(self, u, v):
if u not in self.graph:
self.graph[u] = []
if v not in self.graph:
self.graph[v] = []
self.graph[u].append(v)
self.graph[v].append(u) # 无向图,双向添加
def dfs(self, start):
visited = set()
path = []
self._dfs_util(start, visited, path)
return path
def _dfs_util(self, vertex, visited, path):
visited.add(vertex)
path.append(vertex) # 记录路径
for neighbor in self.graph.get(vertex, []):
if neighbor not in visited:
self._dfs_util(neighbor, visited, path)
# 创建无向图
G = Graph()
edges = [
('A', 'B'), ('A', 'C'), ('B', 'D'), ('B', 'E'),
('C', 'E'), ('C', 'F'), ('D', 'E'), ('E', 'F'),
('E', 'G'), ('F', 'H'), ('G', 'I'), ('H', 'I')
]
for u, v in edges:
G.add_edge(u, v)
# 执行 DFS
dfs_result = G.dfs('A')
print("深度优先搜索的结果:", dfs_result)
# 深度优先搜索的结果: ['A', 'B', 'D', 'E', 'C', 'F', 'H', 'I', 'G']