關於深度優先遍歷圖的非遞歸算法的一個討論


參考:

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("\n");
114     //    DFS1(G,0);
115     //    printf("\n");
116     //    BFS(G,0);
117     //    printf("\n");
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再一次 入棧。所以算法一不正確。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM