递归之深度优先搜索

Posted guohaoblog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了递归之深度优先搜索相关的知识,希望对你有一定的参考价值。

递归的思想在写程序中运用较为广泛,看视很复杂的问题,通常通过递归思想找出“递归结构”,分解成重复的小步骤即可解决,但是递归的思想有时并不好理解(大佬,悟性高的忽略)。本文通过介绍全排序例子介绍递归思想,最后给出前一次博客写的”坑爹的奥数“问题进行递归优化,给出执行时间。

 

一、问题描述:假如有编号为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("
");     
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之间的整数	");
52     scanf("%d",&n);   // 输入的时候需要主要n为1~9之间的整数 
53     dfs(1);     // 首先站在1号小盒子面前 
54     printf("
"); 
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
",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("
total=%d

",total/2);   // 因为输出的有重复的 比如 173+286=459 与 286+173=459是一样的 
40     printf("total cost time:%lfs
",(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
",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("
total=%d

",total/2); 
38     printf("total cost time:%lfs
",(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
",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("
total=%d

",total/2); 
44     printf("total cost time:%lfs
",(end-start)/1000);
45     return 0;
46 }

执行结果(部分截图):

技术分享图片

 

通过比较运行时间,想必大家知道针对统一问题,不同算法效率差别还是挺大的,虽说递归的效率不高,但是在针对小规模数据时,表现还是可以的 。对递归的深入理解可以多看看大咖博客,自己断点跟踪调试。

 

以上是关于递归之深度优先搜索的主要内容,如果未能解决你的问题,请参考以下文章

数据结构与算法图遍历算法 ( 深度优先搜索 DFS | 深度优先搜索和广度优先搜索 | 深度优先搜索基本思想 | 深度优先搜索算法步骤 | 深度优先搜索理论示例 )

最大岛屿之深度优先搜索(dfs)

深度优先搜索

深度优先搜索和广度优先搜索的比较与分(转)

多级树 深度优先搜索算法和广度优先搜索算法

python 递归深度优先搜索与广度优先搜索算法模拟实现