深度優先搜索(DFS)遞歸形式改為非遞歸形式


DFS將遞歸改為非遞歸這個方法的需求來自於一道三維積木組合的題目,還在苦苦調試中,暫且不提。

普通的認識對於遞歸向非遞歸的轉化無非是使用棧,但是結合到深度搜索如何將棧很好利用,如何很好保存現場,都不是很輕松(自身感覺)。

網上大部分轉化都是基於圖的搜索進行,總是引出鄰接點的概念,讓人越看越迷,畢竟不是每個DFS都是圖(不可否認都可以看成是圖)。

在眾多資料中看到了CSDN上的一個轉化方法很新穎(結構之法,算法之道):http://blog.csdn.net/v_july_v/article/details/6111353

最后一點結合圖提出了用隊列棧來進行轉化,由於偽代碼和圖有關,而且用到標志什么的,並沒有細看,但是這個思想倒是啟發了我。於是我決定使用這個思想進行轉化嘗試。

全排列問題是一個典型的可利用DFS搜索出結果的題目,正巧我們學校的OJ上有這個題目的評測:http://acm.xmu.edu.cn/JudgeOnline/problem.php?id=1005

於是使用隊列棧來進行全排列,核心思想是:

1.使用每一個隊列表示深度搜索的同一層節點。

2.棧的關系表示的是父親和兒子的關系,不同層節點,且底部棧表示父親節點,上層棧表示兒子節點

整個非遞歸DFS過程如下:

1.初始化最底層棧

2.只要棧內還有隊列繼續循環(3-5):

3.將棧頂隊列彈出:

4.判斷棧頂隊列是否為空,若為空,進行恢復現場操作,並且往回回溯,若不為空,將棧頂隊列首元素出棧,為該元素生成下層節點,也為一個隊列,然后將該元素作為已經遍歷的一部分,記錄到結果中。

5.判斷生成的隊列是否為空,為空,說明已經到了搜索最底層,可輸出相應的解,若不為空,將此隊列入棧。

這里有兩個注意點:

1.恢復現場操作有兩處:一處在放置結果的時候,一處為棧頂隊列為空的時候

2.在第四步將棧頂隊列首元素出棧之后,這個隊列有可能為空,在這里不需要對這個隊列進行和第5步類似的操作。因為有可能出現該節點為空,而兒子並不為空的情況。

 1 #include<iostream>
 2 #include<stack>
 3 #include<queue>
 4 using namespace std;
 5 int n;
 6 int ans[12];
 7 int visited[12]={0};
 8 typedef struct point{
 9     int num;
10 }Point;
11 stack< queue<Point> > mainstack;
12 void DFS()
13 {
14     
15     int cur=1;
16     queue<Point> oneq;
17     for(int i=1;i<=n;i++)
18     {
19         Point oneP;
20         oneP.num=i;
21         oneq.push(oneP);
22     }
23     mainstack.push(oneq);
24     while(!mainstack.empty())
25     {
26         queue<Point> twoq;
27         twoq=mainstack.top();mainstack.pop();
28         if(!twoq.empty())
29         {
30             Point twoP=twoq.front();twoq.pop();
31             int onenum=twoP.num;
32             visited[ans[cur]]=0;//1.如果要修改則將當前置為可用
33             ans[cur]=onenum;
34             visited[onenum]=1;
35             queue<Point> threeq;//該節點的子節點
36             for(int i=1;i<=n;i++)
37             {
38                 if(visited[i]==0)
39                 {
40                     Point threep;
41                     threep.num=i;//threep.flag=i;
42                     threeq.push(threep);
43                 }
44             }
45             //在這里直接加空判斷,會出現本節點兄弟為空,兒子不為空的情況
46             mainstack.push(twoq);
47             //沒有可擴展節點
48             if(threeq.empty())
49             {
50                 for(int i=1;i<=n;i++)
51                     cout<<ans[i]<<" ";
52                 cout<<endl;
53             }
54             else
55             {
56                 mainstack.push(threeq);
57                 cur++;
58             }
59         }
60         else
61         {
62             visited[ans[cur]]=0;
63             ans[cur]=0;//這里置0才能完全還原
64             cur--;
65         }
66     }
67 }
68 int main()
69 {
70     cin>>n;
71     DFS();
72     return 0;
73 }
全排序非遞歸

 

至於到全排序查重的地方,應該還有可以優化的地方,暫且不提,此代碼在XOJ上提交通過。

56 K
1072 MS
G++
Apple

 

 


免責聲明!

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



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