图的遍历
遍历定义:从已给的连通图中某一顶点出发,沿着一些边访遍图中所有的顶点,且使每个顶点仅被访问一次,就叫做图的遍历,它是图的基本运算
遍历的实质:找每个顶点的邻接点的过程
图的特点:图中可能存在回路,且图的任一顶点都可能与其他顶点想通,在访问完某个顶点之后可能会沿着某些边又回到了曾经访问过的顶点
怎样避免重复访问?
解决思路:设置辅助数组
v
i
s
i
t
e
d
[
n
]
visited[n]
visited[n],用来标记每个被访问过的顶点
- 初始状态 v i s i t e d [ i ] visited[i] visited[i] 为0
- 顶点 i i i 被访问,改 v i s i t e d [ i ] visited[i] visited[i] 为1,防止被多次访问
图常用的遍历:
- 深度优先搜索(Depth_First Search——DFS)
- 广度优先搜索(Breadth_First Search——BFS)
结论:
稠密图适于在邻接矩阵上进行深度遍历
稀疏图适于在邻接表上进行深度遍历
深度优先遍历(DFS)
DFS算法效率分析
- 用邻接矩阵来表示图,遍历图中每一个顶点都要从头扫描该顶点所在行,时间复杂度为 O ( n 2 ) O(n^2) O(n2)
- 用邻接表来表示图,虽然有 2 e 2e 2e 个结点,但只需扫描 e e e 个结点即可完成遍历,加上访问 n n n 个头结点的时间,时间复杂度为 O ( n + e ) O(n+e) O(n+e)
深度优先遍历算法的实现
- 初始状态 v i s i t e d [ i ] visited[i] visited[i] 为0
- 顶点i被访问,改 v i s i t e d [ i ] visited[i] visited[i] 为1,防止被多次访问
算法实现:
void DFS(AMGraph G, int v){ //图G为邻接矩阵类型
cout << v; //访问第v个顶点
visited[v] = true;
for(w = 0; w < G.vexnum; w++) //依次检查邻接矩阵v所在的行
if((G.arcs[v][w] != 0) && (!visited[w])) //w是v的邻接点,如果w未访问,则递归调用DFS
DFS(G, w)
}
广度优先搜索遍历(BFS)
方法:
从图的某一结点出发,首先依次访问该结点的所有邻接顶点
v
i
1
vi_1
vi1,
v
i
2
vi_2
vi2,…
v
i
n
vi_n
vin,再按只写顶点被访问的先后次序依次访问与他们相邻接的所有未被访问的顶点;
重复此过程,直至所有顶点均被访问为止
实现过程的讲解:https://www.bilibili.com/video/BV1nJ411V7bd?p=125
算法实现:
//按该广度优先非递归遍历连通图G
void BFS(Graph G, int v){
cout << v;
visited[v] = ture; //访问第v个顶点
InitQueue(Q); //辅助队列Q初始化,置空
EnQueue(Q, v); //v进队
while(!QueueEmpty(Q)){ //队列非空
DeQueue(Q, u); //队头元素出队并置为u
for(w = FirstAdjVex(G,u); w >= 0; w = NextAdjVex(G, u, w))
if(!visited[w]){ //w为u的尚未访问的邻接顶点
cout << w;
visited[w] = ture;
Enqueue(Q, w); //w进队
}//if
}//while
}//BFS
BFS算法效率分析
- 如果使用邻接矩阵,则BFS对于每一个被访问到的顶点,都要循环检测矩阵中的整整一行( n n n个元素),总的时间代价为 O ( n 2 ) O(n^2) O(n2)
- 用邻接表来表示图,虽然有
2
e
2e
2e 个结点,但只需扫描
e
e
e个结点即可完成遍历,加上访问n个头结点的时间,时间复杂度为
O
(
n
+
e
)
O(n+e)
O(n+e)
DFS与BFS算法比较
- 空间复杂度相同,都是 O ( n ) O(n) O(n)(借用了堆栈或队列)
- 时间复杂度只与存储结构(邻接矩阵或邻接表)有关,而与搜索路径无关