遞歸之深度優先搜索


遞歸的思想在寫程序中運用較為廣泛,看視很復雜的問題,通常通過遞歸思想找出“遞歸結構”,分解成重復的小步驟即可解決,但是遞歸的思想有時並不好理解(大佬,悟性高的忽略)。本文通過介紹全排序例子介紹遞歸思想,最后給出前一次博客寫的”坑爹的奧數“問題進行遞歸優化,給出執行時間。

 

一、問題描述:假如有編號為1、2、3的3張撲克牌和編號為1、2、3的3個盒子。現在需要將這3張撲克牌分別放到3個盒子里面,並且每個盒子有且只有一張撲克牌。那么一共有多少種不同的放法呢?

當有n個數字n個盒子(每個數字取值1~n之間且不重復),那么有多少種不同的放法呢?

輸入:n  -------  表示n個盒子

輸出:全排序  和  排序總數

 

二、代碼示例(帶有詳細注釋)

 1 #include <stdio.h>
 2  
 3 int a[10]={0},book[10]={0},n;
 4 
 5 void dfs(int step)   // step表示現在站在第幾個盒子面前
 6 {
 7     int i;
 8     if(step==n+1)   // 如果站在第n+1個盒子面前,則表示前n個盒子已經放好撲克牌
 9     {
10         // 輸出一種排序(1~n號盒子的撲克牌編號)
11         for(i=1;i<=n;++i)
12             printf("%d",a[i]);
13         printf("\n");     
14     } 
15     
16     // 此時站在第step個盒子面前,應該放哪張牌呢?
17     // 按照1,2,3,。。。,n的順序一一嘗試
18     for(i=1;i<=n;++i)
19     {
20         // 判斷撲克牌i是否還在手上
21         if(book[i]==0)    // book[i]等於0表示i號撲克牌在手上
22         {
23             // 開始嘗試使用撲克牌
24             a[step] = i;    // 將i號撲克牌放入到第step個盒子中
25             book[i] = 1;    // 將book[i]設為1,表示i號撲克牌已經不再手上
26             
27             // 第step個盒子已經放好撲克牌沒接下來需要走到下一個盒子面前
28             dfs(step+1);   // 這里通過函數的遞歸調用來實現(自己調用自己)
29             book[i] = 0;    // 這是非常關鍵的一步,一定要將剛才嘗試的撲克牌收回,才能進行下一次嘗試 
30         } 
31     } 
32     return; 
33 } 
34 
35 /*
36     深度優先搜索的基本模型
37     void dfs(int step)
38     {
39         判斷邊界
40         嘗試每一種可能  
41         for(i=1;i<=n;++i)
42         {
43             繼續下一步 dfs(step+1); 
44         } 
45         返回  return ;
46     } 
47 */
48 
49 int main()
50 {
51     printf("請輸入1~9之間的整數\t");
52     scanf("%d",&n);   // 輸入的時候需要主要n為1~9之間的整數 
53     dfs(1);     // 首先站在1號小盒子面前 
54     printf("\n"); 
55     return 0;
56 }

運行結果如下:

 

核心代碼不超過20行,也就是遞歸的思想 ,模板已給出。關於遞歸思想可以看《程序員數學》第六章,個人感覺寫的很好。下面介紹前面介紹的“坑爹的奧數問題”給出遞歸代碼並與其他算法比較

問題描述:https://www.cnblogs.com/guohaoblog/p/9255706.html

方法一:枚舉法(嵌套多重循環)

 1 #include <stdio.h>
 2 #include <time.h> 
 3 /*
 4   有9個方格 三個一組構成一個數字,兩個數相加等於第三個數,這9個數從1~9中選取
 5   且每個數字只能使用一次使得等式成立  例如 173+286=459   
 6   請問一種有多少中合理的組合? 
 7 */ 
 8 
 9 int main(int argc, char *argv[])
10 {
11     int a[10],i,total=0;   // a[1]~a[9] 表示這9個數 
12     double start,end;
13     start = clock();
14     for(a[1]=1;a[1]<=9;++a[1])  // 第一個數的百位
15     for(a[2]=1;a[2]<=9;++a[2])  // 第一個數的十位 
16     for(a[3]=1;a[3]<=9;++a[3])  // 第一個數的個位
17     for(a[4]=1;a[4]<=9;++a[4])  // 第二個數的百位
18     for(a[5]=1;a[5]<=9;++a[5])  // 第二個數的十位
19     for(a[6]=1;a[6]<=9;++a[6])  // 第二個數的個位
20     for(a[7]=1;a[7]<=9;++a[7])  // 第三個數的百位
21     for(a[8]=1;a[8]<=9;++a[8])  // 第三個數的十位
22     for(a[9]=1;a[9]<=9;++a[9])  // 第三個數的個位
23     {   // 接下來判斷每一位上的數互不相等
24         if(a[1]!=a[2] && a[1]!=a[3] && a[1]!=a[4] && a[1]!=a[5] && a[1]!=a[6] && a[1]!=a[7] && a[1]!=a[8] && a[1]!=a[9]
25                       && a[2]!=a[3] && a[2]!=a[4] && a[2]!=a[5] && a[2]!=a[6] && a[2]!=a[7] && a[2]!=a[8] && a[2]!=a[9]
26                                     && a[3]!=a[4] && a[3]!=a[5] && a[3]!=a[6] && a[3]!=a[7] && a[3]!=a[8] && a[3]!=a[9]
27                                                   && a[4]!=a[5] && a[4]!=a[6] && a[4]!=a[7] && a[4]!=a[8] && a[4]!=a[9]
28                                                                 && a[5]!=a[6] && a[5]!=a[7] && a[5]!=a[8] && a[5]!=a[9]
29                                                                                && a[6]!=a[7] && a[6]!=a[8] && a[6]!=a[9]
30                                                                                                && a[7]!=a[8] && a[7]!=a[9]                                                                                                         && a[8]!=a[9]
31             
32             && a[1]*100+a[2]*10+a[3] + a[4]*100+a[5]*10+a[6] == a[7]*100+a[8]*10+a[9] )
33             {
34                 total++;
35                 printf("%d%d%d+%d%d%d=%d%d%d\n",a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9]);
36             }     
37     } 
38     end = clock();
39     printf("\ntotal=%d\n\n",total/2);   // 因為輸出的有重復的 比如 173+286=459 與 286+173=459是一樣的 
40     printf("total cost time:%lfs\n",(end-start)/1000);
41     return 0;
42 }

執行結果部分截圖:

 

方法二:采用標記(還是枚舉法)

 1 #include <stdio.h>
 2 #include <time.h>
 3 
 4 int main(int argc, char *argv[])
 5 {
 6     //  算法改進  用book來標記互不相等的數
 7     int a[10],i,total=0,book[10],sum;
 8     double start,end;
 9     start = clock();
10     for(a[1]=1;a[1]<=9;++a[1])  // 第一個數的百位
11     for(a[2]=1;a[2]<=9;++a[2])  // 第一個數的十位 
12     for(a[3]=1;a[3]<=9;++a[3])  // 第一個數的個位
13     for(a[4]=1;a[4]<=9;++a[4])  // 第二個數的百位
14     for(a[5]=1;a[5]<=9;++a[5])  // 第二個數的十位
15     for(a[6]=1;a[6]<=9;++a[6])  // 第二個數的個位
16     for(a[7]=1;a[7]<=9;++a[7])  // 第三個數的百位
17     for(a[8]=1;a[8]<=9;++a[8])  // 第三個數的十位
18     for(a[9]=1;a[9]<=9;++a[9])  // 第三個數的個位
19     {
20         for(i=1;i<10;++i)   // 初始化book數組
21             book[i]=0;
22         for(i=1;i<10;++i)   // 如果某個數出現過就標記一下 
23             book[a[i]]=1;     
24             
25         // 統計共出現了多少個不同的數
26         sum = 0;
27         for(i=1;i<10;++i)
28             sum+=book[i];
29         // 如果正好出現了9個不同的數,並且滿足等式條件,則輸出
30         if(sum == 9 &&  a[1]*100+a[2]*10+a[3] + a[4]*100+a[5]*10+a[6] == a[7]*100+a[8]*10+a[9])
31         {
32             total++;
33             printf("%d%d%d+%d%d%d=%d%d%d\n",a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9]);
34         }        
35     } 
36     end = clock(); 
37     printf("\ntotal=%d\n\n",total/2); 
38     printf("total cost time:%lfs\n",(end-start)/1000);
39     return 0;
40 }

執行結果(部分截圖):

 

  方法三:遞歸調用

 1 #include <stdio.h>
 2 #include <time.h>
 3 
 4 int a[10]={0},i,total=0,book[10]={0};
 5 
 6 void dfs(int step)   // step表示現在站在第幾個盒子面前
 7 {
 8     int i;
 9     if(step==10)  // 如果站在第10個盒子面前,則表示前9個盒子已經放好撲克牌
10     {
11         if(a[1]*100+a[2]*10+a[3] + a[4]*100+a[5]*10+a[6] == a[7]*100+a[8]*10+a[9])
12         {
13             total++;
14             printf("%d%d%d+%d%d%d=%d%d%d\n",a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9]);
15         }    
16         return;         // 返回之前的一步(最近調用的地方) 
17     } 
18     
19     // 此時站在第step個盒子面前,應該放哪張牌呢?
20     // 按照1,2,3,。。。,n的順序一一嘗試
21     for(i=1;i<=9;++i)
22     {
23         // 判斷撲克牌i是否還在手上
24         if(book[i]==0) // book[i]為0表示撲克牌還在手上
25         {
26             // 開始嘗試使用撲克牌i
27             a[step] = i;  // 將撲克牌i放入到第step個盒子中
28             book[i] = 1;
29             
30             dfs(step+1);
31             book[i]=0; 
32         } 
33     } 
34     return;
35 } 
36 
37 int main(int argc, char *argv[])
38 {
39     double start,end;
40     start = clock();
41     dfs(1);    // 首先站在一個盒子面前 
42     end = clock(); 
43     printf("\ntotal=%d\n\n",total/2); 
44     printf("total cost time:%lfs\n",(end-start)/1000);
45     return 0;
46 }

執行結果(部分截圖):

 

通過比較運行時間,想必大家知道針對統一問題,不同算法效率差別還是挺大的,雖說遞歸的效率不高,但是在針對小規模數據時,表現還是可以的 。對遞歸的深入理解可以多看看大咖博客,自己斷點跟蹤調試。

 


免責聲明!

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



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