图的遍历递归和非递归实现【整理自网络】
参考:
http://www.cnblogs.com/dolphin0520/archive/2011/07/13/2105236.html
https://www.cnblogs.com/fickleness/archive/2013/09/26/3340677.html
https://yq.aliyun.com/articles/49140 (源地址是http://www.cnblogs.com/kubixuesheng/p/4399705.html)
https://blog.csdn.net/plu_mik/article/details/42177515
http://blog.csdn.net/ivan_zgj/article/details/51541447
图的遍历有两种遍历方式:深度优先遍历(depth-first search)和广度优先遍历(breadth-first search)。
因为深度优先需要无路可走时按照来路往回退,正好是后进先出
广度优先则需要保证先访问顶点的未访问邻接点先访问,恰好就是先进先出
1.深度优先遍历
基本思想:首先从图中某个顶点v0出发,访问此顶点,然后依次从v0相邻的顶点出发深度优先遍历,直至图中所有与v0路径相通的顶点都被访问了;若此时尚有顶点未被访问,则从中选一个顶点作为起始点,重复上述过程,直到所有的顶点都被访问。可以看出深度优先遍历是一个递归的过程。
如下图中的一个无向图
其深度优先遍历得到的序列为:
0->1->3->7->4->2->5->6
2.广度优先遍历
基本思想:首先,从图的某个顶点v0出发,访问了v0之后,依次访问与v0相邻的未被访问的顶点,然后分别从这些顶点出发,广度优先遍历,直至所有的顶点都被访问完。
如上面图中
其广度优先遍历得到的序列为:
0->1->2->3->4->5->6->7
大概代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<queue> 3 #include<stack> 4 #include<stdlib.h> 5 #define MAX 100 6 using namespace std; 7 8 typedef struct 9 { 10 int edges[MAX][MAX]; //邻接矩阵 11 int n; //顶点数 12 int e; //边数 13 }MGraph; 14 15 bool visited[MAX]; //标记顶点是否被访问过 16 17 void creatMGraph(MGraph &G) //用引用作参数 18 { 19 int i,j; 20 int s,t; //存储顶点编号 21 int v; //存储边的权值 22 for(i=0;i<G.n;i++) //初始化 23 { 24 for(j=0;j<G.n;j++) 25 { 26 G.edges[i][j]=0; 27 } 28 visited[i]=false; 29 } 30 for(i=0;i<G.e;i++) //对矩阵相邻的边赋权值 31 { 32 scanf("%d %d %d",&s,&t,&v); 33 //两个顶点确定一条边 34 //输入边的顶点编号以及权值 35 G.edges[s][t]=v; 36 } 37 } 38 39 void DFS(MGraph G,int v) //深度优先搜索 40 { 41 int i; 42 printf("%d ",v); //访问结点v 43 visited[v]=true; 44 for(i=0;i<G.n;i++) //访问与v相邻的未被访问过的结点 45 { 46 if(G.edges[v][i]!=0&&visited[i]==false) 47 { 48 DFS(G,i);//若没访问则继续,而且根据顶点的序号按数序访问 49 } 50 } 51 } 52 //stack弹出顺序有问题 53 void DFS1(MGraph G,int v) //非递归实现 54 { 55 stack<int> s; 56 printf("%d ",v); //访问初始结点 57 visited[v]=true; 58 s.push(v); //入栈 59 while(!s.empty()) 60 { 61 int i,j; 62 i=s.top(); //取栈顶顶点 63 for(j=0;j<G.n;j++) //访问与顶点i相邻的顶点 64 { 65 if(G.edges[i][j]!=0&&visited[j]==false) 66 { 67 printf("%d ",j); //访问 68 visited[j]=true; 69 s.push(j); //访问完后入栈 70 break; //找到一个相邻未访问的顶点,访问之后则跳出循环 71 } 72 } 73 //对于节点4,找完所有节点发现都已访问过或者没有临边,所以j此时=节点总数,然后把这个4给弹出来 74 直到弹出1,之前的深度搜索的值都已弹出,有半部分还没有遍历,开始遍历有半部分 75 if(j==G.n) //如果与i相邻的顶点都被访问过,则将顶点i出栈 76 s.pop(); 77 } 78 } 79 80 void BFS(MGraph G,int v) //广度优先搜索 81 { 82 queue<int> Q; //STL模板中的queue 83 printf("%d ",v); 84 visited[v]=true; 85 Q.push(v); 86 while(!Q.empty()) 87 { 88 int i,j; 89 i=Q.front(); //取队首顶点 90 Q.pop();//弹出一个,然后遍历这个节点的子节点,然后遍历完再弹出下一个 91 for(j=0;j<G.n;j++) //广度遍历 92 { 93 if(G.edges[i][j]!=0&&visited[j]==false) 94 { 95 printf("%d ",j); 96 visited[j]=true; 97 Q.push(j); 98 } 99 } 100 } 101 } 102 103 int main(void) 104 { 105 int n,e; //建立的图的顶点数和边数 106 while(scanf("%d %d",&n,&e)==2&&n>0) 107 { 108 MGraph G; 109 G.n=n; 110 G.e=e; 111 creatMGraph(G); 112 DFS(G,0); 113 printf("\n"); 114 // DFS1(G,0); 115 // printf("\n"); 116 // BFS(G,0); 117 // printf("\n"); 118 } 119 return 0; 120 }
---------------------------------------------------------------------------------
深度优先遍历(DFS):
1、访问指定的起始顶点;
2、若当前访问的顶点的邻接顶点有未被访问的,则任选一个访问之;反之,退回到最近访问过的顶点;直到与起始顶点相通的全部顶点都访问完毕;
3、若此时图中尚有顶点未被访问,则再选其中一个顶点作为起始顶点并访问之,转 2; 反之,遍历结束。
连通图的深度优先遍历类似于树的先根遍历
如何判别V的邻接点是否被访问?
解决办法:为每个顶点设立一个“访问标志”。首先将图中每个顶点的访问标志设为 FALSE, 之后搜索图中每个顶点,如果未被访问,则以该顶点为起始点,进行深度
优先遍历,否则继续检查下一顶点。
![](http://images0.cnblogs.com/blog2015/682679/201504/081430408995109.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081431311339728.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081432122434169.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081432520403811.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081433333369194.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081434179612261.png)
顶点的访问序列为: v0 , v1 , v4 , v5 , v6 , v2 , v3(不唯一)
实现过程:依靠栈,一维数组和图的邻接矩阵存储方式
![](http://images0.cnblogs.com/blog2015/682679/201504/081435169934860.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081435533211861.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081436458689807.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081438226339879.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081438542908941.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081439565241196.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081440144152896.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081441325863181.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081441388052848.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081442546025292.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081443087114381.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081444186968099.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081444280082222.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081449102112141.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081449154153751.png)
![](http://images0.cnblogs.com/blog2015/682679/201504/081450209152474.png)
遍历图的过程实质上是对每个顶点查找其邻接点的过程,所耗费的时间取决于所采用的存储结构。
对图中的每个顶点至多调用1次DFS算法,因为一旦某个顶点已访问过,则不再从它出发进行搜索。
邻接链表表示:查找每个顶点的邻接点所需时间为O(e),e为边(弧)数,算法时间复杂度为O(n+e)
数组表示:查找每个顶点的邻接点所需时间为O(n2),n为顶点数,算法时间复杂度为O(n2)
代码如下
1 //访问标志数组 2 int visited[MAX] = {0}; 3 4 //用邻接表方式实现深度优先搜索(递归方式) 5 //v 传入的是第一个需要访问的顶点 6 void DFS(MGraph G, int v) 7 { 8 //图的顶点的搜索指针 9 ArcNode *p; 10 //置已访问标记 11 visited[v] = 1; 12 //输出被访问顶点的编号 13 printf("%d ", v); 14 //p指向顶点v的第一条弧的弧头结点 15 p = G.vertices[v].firstarc; 16 while (p != NULL) 17 { 18 //若p->adjvex顶点未访问,递归访问它 19 if (visited[p->adjvex] == 0) 20 { 21 DFS(G, p->adjvex); 22 } 23 //p指向顶点v的下一条弧的弧头结点 24 p = p->nextarc; 25 } 26 }
广度优先搜索(BFS)
方法:从图的某一结点出发,首先依次访问该结点的所有邻接顶点 Vi1, Vi2, …, Vin 再按这些顶点被访问的先后次序依次访问与它们相邻接的所有未被访问的顶点,重复此过程,直至所有顶点均被访问为止。
顶点的访问次序
实现过程:依靠队列和一维数组来实现
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include<queue> 3 using namespace std; 4 5 const int MAX = 10; 6 //辅助队列的初始化,置空的辅助队列Q,类似二叉树的层序遍历过程 7 queue<int> q; 8 //访问标记数组 9 bool visited[MAX]; 10 //图的广度优先搜索算法 11 void BFSTraverse(Graph G, void (*visit)(int v)) 12 { 13 int v = 0; 14 //初始化访问标记的数组 15 for (v = 0; v < G.vexnum; v++) 16 { 17 visited[v] = false; 18 } 19 //依次遍历整个图的结点 20 for (v = 0; v < G.vexnum; v++) 21 { 22 //如果v尚未访问,则访问 v 23 if (!visited[v]) 24 { 25 //把 v 顶点对应的数组下标处的元素置为真,代表已经访问了 26 visited[v] = true; 27 //然后v入队列,利用了队列的先进先出的性质 28 q.push(v); 29 //访问 v,打印处理 30 cout << q.back() << " "; 31 //队不为空时 32 while (!q.empty()) 33 { 34 //队头元素出队,并把这个出队的元素置为 u,类似层序遍历 35 Graph *u = q.front(); 36 q.pop(); 37 //w为u的邻接顶点 38 for (int w = FirstAdjVex(G, u); w >= 0; w = NextAdjVex(G,u,w)) 39 { 40 //w为u的尚未访问的邻接顶点 41 if (!visited[w]) 42 { 43 visited[w] = true; 44 //然后 w 入队列,利用了队列的先进先出的性质 45 q.push(w); 46 //访问 w,打印处理 47 cout << q.back() << " "; 48 }//end of if 49 }//end of for 50 }//end of while 51 }//end of if 52 }// end of for 53 }