图的遍历
-
含义: 从已给的连通图中的某一顶点出发,沿着一些边访遍图中所有的顶点,且使每个顶点仅被访问一次,就叫做图的遍历。
-
遍历实质: 找每个顶点的邻接点的过程。
-
图的特点:
图中可能存在回路,且图的任一顶点都可能与其它顶点相通,在访问完某个顶点之后可能会沿着某些边又回到了曾经访问过的顶点。 -
如何避免重复访问?
设置辅助数组visited[n],用来标记每个被访问过的顶点。初始状态visited[i]为0,当顶点i被访问,改visited[i]为1,防止被多次访问。 -
图的常用遍历:
- 深度优先搜索(Depth First Search——DFS)
- 广度优先搜索(Breadth First Search——BFS)
DFS思想
-
方法
- 在访问图中某一起始顶点v后,由v出发,访问它的任一邻接顶点 w 1 w_1 w1;
- 再从 w 1 w_1 w1出发,访问与 w 1 w_1 w1邻接但还未被访问过的顶点 w 2 w_2 w2;
- 然后再从 w 2 w_2 w2出发,进行类似的访问,…
- 如此进行下去,直至到达所有的邻接顶点都被访问过的顶点u为止;
- 接着,退回一步,退到前一次刚访问过的顶点,看是否还有其它没有被访问的邻接顶点。
- 如果有,则访问该顶点,之后再从此顶点出发,进行与前述类似的访问;
- 如果没有,就再退回一步进行搜索。重复上述过程,直至连通图中所有顶点都被访问过为止。
-
例子:
连通图的DFS类似于树的先根遍历。
DFS——邻接矩阵实现
-
无向图的DFS实现:
-
具体过程描述:
1.设置辅助数组visited,里面的值全为0,表示初始时每个顶点都没有被访问过。
2.假设从结点2开始访问,将结点2的visited数组的值置1,表示2号结点被访问过,接着查看邻接矩阵,查找结点2的邻接点有哪些,即顺序查找邻接矩阵中行下标为2(第2行)的,值为1的并且没有被访问过的(visited数组值为0的),发现结点1满足要求,所以访问完结点2之后就访问结点1。
3.再从结点1出发,执行步骤2,即查找邻接矩阵第1行中值为1的,此时,结点2值为1,但是不行,因为结点2被访问过,然后再看结点3,发现,满足要求,所以访问完结点1之后就访问结点3。
4.再从结点3出发,查找邻接矩阵,发现结点1和结点5的值为1,但结点1已经被访问过,而结点5满足要求,所以访问完结点3之后就访问结点5。
5.再从结点5出发,查找邻接矩阵,发现其邻接点为结点2和结点3,但是结点2和3都被访问过,不满足要求。所以此时就要往回退,退到3号结点,发现3号结点的2个邻接点也都被访问过了。继续往回退,推到结点1,发现结点4满足要求,所以访问完结点5之后就访问结点4。
6…再从结点4出发,查找邻接矩阵,发现结点6满足要求,所以访问完结点4之后就访问结点6。
7.此时发现,所有结点均已访问过,结束。
8.返回遍历顺序, 2 → 1 → 3 → 5 → 4 → 6 {\rm{2}} \to {\rm{1}} \to {\rm{3}} \to {\rm{5}} \to {\rm{4}} \to {\rm{6}} 2→1→3→5→4→6。 -
算法描述:
void DFS(AMGraph G, int v)
{
cout << v; //访问第v个顶点
visited[v] = true;
for (w = 0; w < G.vexnum; w++) //依次检查邻接矩阵v所在的行
if ((G.arcs[v][w] != 0) && (!visited[w]))
DFS(G, w); //w是v的邻接点,如果w未被访问,则递归调用DFS
}
用邻接矩阵来表示图,遍历图中的每一个顶点都要从头扫描该顶点所在行,时间复杂度为 O ( n 2 ) O(n^2) O(n2)。
用邻接表表示图,虽然有2e个结点,但只需扫描e个结点即可完成遍历,加上访问n个头结点的时间,时间复杂度为 O ( n + e ) O(n+e) O(n+e)。
所以,稠密图适于在邻接矩阵上进行深度遍历;稀疏图适于在邻接表上进行深度遍历。
非连通图的DFS
通过范访问过程中栈空的次数可以判断该非连通图里有几个连通分量。