# 图的遍历 --- 图的遍历:从图的某个顶点出发,按照某种搜索方法沿着图的边访问所有顶点,每个顶点仅访问一次。 遍历得到的顶点的序列,称为图遍历序列。显然不同的搜索方法会得到不同的遍历序列。 有两种搜索方法: * 深度优先搜索(DFS:Depth First Search) * 广度优先搜索(BFS:Breadth First Search) --- ## 2、深度优先搜索 从某个顶点v0出发,找到其第一个邻接点v1,从v1开始继续进行深度优先搜索,如果某个顶点没有未访问的邻接点,就沿着原路回溯,一直到所有的顶点都被遍历到(既回溯到开始搜索的地方)。 ![](https://cdn.itbegin.com//pics/admin/2019/4/2fb7c94c6b8841959e2f9acd9f9e79d7.jpg) --- 上图从A点出发做dfs搜索(蓝字表示访问,红字表示回溯): 1. 访问A点,邻接点有B、F、G,选择B点 2. 访问B点,邻接点有C、D,选择C点 3. 访问C点,没有邻接点,返回 4. 回溯到B点,未访问点有D,选择D点 5. 访问D点,邻接点有E,选择E点 6. 访问E点,没有邻接点,返回 7. 回溯到D点,没有未访问点,返回 8. 回溯到B点,没有未访问点,返回 --- 9. 回溯到A点,未访问点有F、G,选择F 10. 访问F点,没有邻接点,返回 11. 回溯到A点,未访问点有G,选择G 12. 访问G点,未访问点有H,选择H 13. 访问H点,没有邻接点,返回 14. 回溯到G点,没有未访问点,返回 15. 回溯到A点,没有未访问点,返回 16. 回到搜索调用的地方,搜索结束 遍历序列为:ABCDEFGH `理解dfs`:尽可能的远离起始点。 --- ## 3、算法实现 `要点描述`: * 根据邻接矩阵(或邻接表)能遍历每个点的邻接点 * 已经访问的点,用一个桶数组vis[]记录,防止重复经过 * 用递归函数实现往下遍历 * 也可以用栈(stack)来实现 ``` dfs(顶点) { 记录访问; for(邻接点) if (没有访问过) dfs(邻接点); } int main(){ dfs(起始点); return 0; } ``` --- 如果图不一定连通,调用dfs的时候需要用到循环 ``` int main() { for(int i=1; i<=n; i++) if(!vis[i]) dfs(i); } ``` --- `注意`: * dfs遍历经过所有点和边,其复杂度为O(V+E) * dfs遍历时没有对状态回溯,如果加上状态回溯就变成了枚举所有的路径 --- ### 扩展理解 dfs使用递归进行遍历,每个顶点都有进入、退出的时机,注意退出时机是和递归调用反着的。 --- ## 4、dfs回溯 在dfs遍历时,往往通过vis数组标记经过的点(或边),这是为了避免回路,这种情况下往往只有一条路径可走。 如果我们想枚举所有的路径,就要对vis状态进行回溯,例如: ``` void dfs(int x) { vis[x] = true; for(int i=0; i
=ans,则再走下去也肯定会比ans大,因此可以直接返回。 ``` void dfs(int x, int sum) { if(sum >= ans) return; //剪枝 if(x == x0) { //求到达x0的最小距离 ans = min(ans, sum); return; } ...... ``` --- # 图的遍历之BFS --- ## 1、队列 队列是一种数据结构,只能从队尾加入元素,从队首弹出元素,特性就是先进入队列的元素先出队,简称FIFO(First In First Out)。 c++的stl里提供了队列容器queue,如下: * 头文件:#include
* 变量:queue
q; * 入队:q.push(x); //插入x到队尾 * 出队:q.pop(); //删除队首的元素(没有获取) * 访问:q.front(); //访问对首的元素 * 判空:q.empty(); * 个数:q.size(); --- ## 2、广度优先搜索 从某个顶点v0出发,依次访问其相邻的每个点,然后逐层向外访问,直到没有可访问点(或找到解)为止。 ![](https://cdn.itbegin.com//pics/admin/2019/4/8954d48bdfd24819962405cf80e0c00d.jpg) --- 上图从A点出发进行bfs遍历(蓝色字表示访问顺序): * 访问A点(出队),邻接点有B、F、G,入队后队列为BFG * 访问B点(出队),邻接点有C、D,入队后队列为FGCD * 访问F点(出队),邻接点无,队列为GCD * 访问G点(出队),邻接点为D、H,入队后队列为CDH(D重复不再入队) * 访问C点(出队),邻接点无,队列为DH * 访问D点(出队),邻接点为E,入队后队列为HE * 访问H点(出队),邻接点无,队列为E * 访问E点(出队),邻接点无,队列为空 * 循环结束 --- bfs遍历序列为:ABFGCDHE 可以发现,整个遍历过程是分层进行的,先遍历距离为1的所有顶点,再距离为2的所有顶点,···,直到访问完毕。在距离为x的层未访问完成之前,不会访问距离为x+1的层。 --- 层次结构如下图: ![](https://cdn.itbegin.com//pics/admin/2018/11/b4031b81ebd64d34a5bd66b7df13ebba.jpg) --- ## 3、算法实现 伪代码: ``` bfs() 起点入队 while (队列不空) 访问队首元素、出队 for 队首元素相邻结点 入队并标记 ``` --- 使用stl的queue容器: ``` void bfs(int v0) { queue
q; vis[v0] = true; q.push(v0); while(!q.empty()) { int tmp = q.front(); cout<
q; vis[sx] = true; q.push(sx); while(!q.empty()) { int x = q.front(); q.pop(); for(int i=0; i