深度优先搜索算法和广度优先搜索算法都是基于“图”这种数据结构的。
作为图的搜索算法,既可用于有向图,也可用于无向图,以下均用无向图讲解。
广度优先搜索
Breadth-First-Search,BFS。
一种“地毯式”层层推进的搜索策略,先查找离起始顶点最近的,然后是次近的,依次往外搜索。
s 表示起始顶点,t 表示终止顶点。搜索一条从 s 到 t 的路径。
实际上,求得的路径就是从 s 到 t 的最短路径。
代码(下面有完整代码)中包含3个重要的辅助变量:
- visited -> list,保存已经访问的顶点,避免重复访问;
- q -> queue,队列,逐层访问。
每次弹出第k层顶点时,将第与k连接的k+1层顶点添加至队尾(添加前会通过visited检测是否已经访问,只入队未访问过的顶点); - prev -> list,保存搜索路径,例如prev[3] = 2,代表顶点3的前驱顶点为顶点2,用于后期打印。
时间复杂度O(n),空间复杂度O(n)。
最坏的情况,s 与 t 相距较远,需要遍历完整的图才能找到,而遍历是不重复的,最多遍历全部顶点,所以复杂度是O(n)。
中间的三个辅助变量,大小均不会超过顶点个数,所以空间复杂度也是O(n)。
深度优先搜索
Depth-First-Search,DFS。
用“走迷宫”举例,随意选择一个岔路口来走,走着走着发现走不通,你就回退到上一个岔路口,重新选择一条路继续走,直到最终找到出口。 这种走法就是一种深度优先搜索策略。
用的是回溯思想,后面会单独讲回溯。
深度优先搜索找出的路径并不是最短路径。
代码中用到的辅助变量:
- visited
- prev
- found -> bool,标识作用,已经找到终止顶点 t 后,不再递归查找。
前两个变量与上面的广度优先遍历一致。
时间复杂度O(n),空间复杂度O(n)。
每条边最多会被访问两次,所以时间复杂度为O(n)。
空间复杂度原因与广度优先搜索一致。
代码
from collections import deque
class Graph:
"""无向图"""
def __init__(self, num_vertices):
# 顶点数量
self._num_vertices = num_vertices
# [[], [], [] ...],内部每个小列表存储该顶点的相连的其他顶点
self._adjacency = [[] for _ in range(num_vertices)]
def add_edge(self