关于深度优先遍历图的非递归算法的一个讨论
参考:
http://www.cnblogs.com/kubixuesheng/p/4399705.html
http://www.cnblogs.com/dolphin0520/archive/2011/07/13/2105236.html
图的深度优先遍历递归算法大概如下:

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 }
图的广度优先遍历算法大概如下:

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 }
上面是用邻接表结构实现的代码。下面是用邻接矩阵实现的代码,包含递归深搜、非递归深搜、广搜,可以参考一下:

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 }
在网络上查了很多资料,又去看了好几本教材,发现这些地方对图的深度优先遍历算法的讲解,基本都是用递归算法实现的。对非递归算法实现描述不是很多。上面第三段代码实现了非递归深搜算法。
在寻找资料过程中,也在思考如何实现,大概设计了如下两种算法:
算法一:(这个是一个错误的思路)
1 st.push(v1)//出发点入栈 2 while(!st.empty()) 3 { 4 temp=st.top(); st.pop();//临时备份栈顶节点到temp,删除栈顶节点 5 printf(temp);//访问temp(原先的栈顶节点) 6 根据temp寻找所有未曾访问的相邻节点,并把这些节点入栈 7 }
算法二:
1 printf(v1);//访问出发节点 2 st.push(v1);//出发点入栈 3 while(!st.empty()) 4 { 5 temp=st.top();//读取栈顶节点到temp 6 循环遍历temp的所有相邻节点: 7 { 8 如果(发现一个未曾访问的相邻节点w): 9 { 10 printf(w);//访问节点w 11 st.push(w);//把w入栈 12 退出循环 13 } 14 } 15 if(temp没有未曾访问的相邻节点) 16 st.pop();//删除栈顶节点 17 }
这两种算法,假如仅仅是对树进行深搜,应该是没有错的。但是对图进行深搜,算法一确实不正确的。看下面这个例子:
对这个图从V0出发做深搜,假如按照算法一操作,假设节点入栈顺序为:V0,V1,V3,……
其中v1和V3入栈时,V0已经出栈。但是取出栈顶V3做访问操作,然后再把V3的相邻未曾访问节点入栈,则会使得V1再一次 入栈。所以算法一不正确。
分类:
203-图论
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2017-03-24 51:古代密码
2014-03-24 欧几里德算法(辗转相除算法)练习