关于深度优先遍历图的非递归算法的一个讨论

参考:

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 }
View Code

图的广度优先遍历算法大概如下:

 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 }
View Code

上面是用邻接表结构实现的代码。下面是用邻接矩阵实现的代码,包含递归深搜、非递归深搜、广搜,可以参考一下:

  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("
");
114     //    DFS1(G,0);
115     //    printf("
");
116     //    BFS(G,0);
117     //    printf("
");
118     }
119     return 0;
120 }
View Code

在网络上查了很多资料,又去看了好几本教材,发现这些地方对图的深度优先遍历算法的讲解,基本都是用递归算法实现的。对非递归算法实现描述不是很多。上面第三段代码实现了非递归深搜算法。

在寻找资料过程中,也在思考如何实现,大概设计了如下两种算法:

算法一:(这个是一个错误的思路

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再一次 入栈。所以算法一不正确。

原文地址:https://www.cnblogs.com/huashanqingzhu/p/8639475.html