深圳幻海软件技术有限公司 欢迎您!

手把手学会DFS (递归入门)

2023-03-14

目录算法介绍递归实现指数型枚举递归实现排列型枚举递归实现组合型枚举算法介绍🧩DFS即DepthFirstSearch ,中文又叫深度优先搜索,是一种沿着树的深度对其进行遍历,直到尽头之后再进行回溯,再走其他路线的方法,在对数据进行枚举,或求子串数量时具有奇效。该算法的实现取决于递归,因此

目录

算法介绍

递归实现指数型枚举

递归实现排列型枚举

递归实现组合型枚举


算法介绍

🧩DFS Depth First Search ,中文又叫深度优先搜索,是一种沿着树的深度对其进行遍历,直到尽头之后再进行回溯,再走其他路线的方法,在对数据进行枚举,或求子串数量时具有奇效。该算法的实现取决于递归,因此如何设置递归的结束条件,以及什么时候调用递归便显得十分重要。

🧩通过 DFS 进行遍历,便可以无遗漏地走遍整个树,再根据题目要求在特定的位置对数据进行处理或输出。

🧩最重要的一点便是,当我们一条路走到底之后,就要回溯,就像上图中 3 5 6 步那样回到上一个节点。之后会判断是走另外一条路还是再回溯到上一层,由于在回溯前我们就已经对数据进行了处理,所以要对数据进行还原,即回溯到第一次到达这个节点时的那份数据。具体的细节就留到题目里面讲吧。

递归实现指数型枚举

传送门:AcWing 92. 递归实现指数型枚举

🧩DFS 的入门题,要求在 1~n 之间找所有可能出现的子序列的情况。可以全选也可以全部都不选,但输出的方式就比较宽松不需要特别处理。

🧩于是我们就可以将 1~n 的每一位都看作有两个选择选和不选,用一个数组存储。每次到达叶节点的时候便完成一次枚举,并输出。而每次递归参数就加一,表示往下一层,位数的增加。注意的细节都写在代码里了,结合代码进行理解。

如果比较难想象一定要画递归图!!!!

  1. #include<cstdio>
  2. #include<string>
  3. #include<iostream>
  4. #include<algorithm>
  5. using namespace std;
  6. const int N = 15;
  7. int n;
  8. int sta[N]; //状态数组,表示当前位的数选或不选
  9. void dfs(int i)
  10. {
  11. if(i == n) //位数等于n,表示所有数的状态都已确定,可以进行输出
  12. {
  13. for(int j = 1;j<=n;j++)
  14. {
  15. if(sta[j] == 1)
  16. {
  17. printf("%d ",j);
  18. }
  19. }
  20. puts("");
  21. return;
  22. }
  23. sta[i+1] = 1; //1表示选该数字
  24. dfs(i+1); //往下一层递归
  25. sta[i+1] = 0; //回溯后重置,但其实下面就会覆盖掉加不加没区别,只是为了好理解
  26. sta[i+1] = 2; //2表示不选
  27. dfs(i+1);
  28. sta[i+1] = 0;
  29. }
  30. int main()
  31. {
  32. scanf("%d",&n);
  33. dfs(0); //从第0层开始
  34. return 0;
  35. }

递归实现排列型枚举

🧩传送门:AcWing 94. 递归实现排列型枚举

🧩依题意,一个 1~n 的整数要输出其所有的排列情况,这便于上一题不同了,上一题是每位数的相对位置都是固定的,输出长度是不固定的。但这题固定长度,但每个位的数字并不是固定的。那我们便可以从每位出发,枚举每一位可能出现的数字,但每个数字都只能出现一次。因此需要一个数组记录这个数字是否被使用过。递归结束时输出当前方案,再进行回溯。

  1. #include<cstdio>
  2. #include<string>
  3. #include<iostream>
  4. #include<algorithm>
  5. using namespace std;
  6. const int N = 10;
  7. int n,count;
  8. int sta[N]; //用于存储方案
  9. bool used[N]; //有没有被用过
  10. void dfs(int i)
  11. {
  12. if(i>n) //从1开始递归则判断条件为i>n从0开始则是i=n结束递归
  13. {
  14. for(int j = 1;j<=n;j++) printf("%d ",sta[j]); //输出
  15. puts("");
  16. return;
  17. }
  18. for(int j = 1;j<=n;j++) //寻找还没有用过的数
  19. {
  20. if(!used[j]) //true表示用过,false表示未用
  21. {
  22. sta[i] = j; //i位置选j
  23. used[j] = true; //标记j已使用
  24. dfs(i+1); //往下一位递归
  25. used[j] = false; //回溯后当前位就不使用了于是置为false
  26. }
  27. } //之后继续寻找下一个没有用过的数
  28. }
  29. int main()
  30. {
  31. scanf("%d",&n);
  32. dfs(1);
  33. return 0;
  34. }

递归实现组合型枚举

🧩传送门:AcWing 93. 递归实现组合型枚举

🧩这题就有点像上面两题的结合版,给定 1~n 的范围输出指定长度的从小到大的全部排列。要注意的是输出的情况是严格递增的,即不会出现中途有数字小于前面数字的情况。所以在查找没用过的数字时,需要从上一位加一开始查找。只有位数达到标准了才输出,因此会筛选掉一些不符合要求的答案。

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. using namespace std;
  6. const int N = 26;
  7. int n, m;
  8. int sta[N]; //用于存储方案
  9. bool used[N]; //有没有被用过
  10. void dfs(int x)
  11. {
  12. if (x > m) //位数符合,可以输出
  13. {
  14. for (int i = 1; i <= m; i++)
  15. {
  16. printf("%d ", sta[i]);
  17. }
  18. printf("\n");
  19. return;
  20. }
  21. for (int i = sta[x - 1] + 1; i <= n; i++) //由于输出是严格递增的由此当前位一定比上一位大
  22. {
  23. if (!used[i]) //当前数未被用则进行操作,否则跳过
  24. {
  25. sta[x] = i; //当前位置选i
  26. used[i] = true; //标记当前位置已使用
  27. dfs(x + 1); //向下递归
  28. used[i] = false; //状态回溯
  29. }
  30. } //之后继续寻找下一个没有用过的数
  31. }
  32. int main()
  33. {
  34. scanf("%d%d", &n, &m);
  35. dfs(1);
  36. return 0;
  37. }

🧩好了这次DFS的入门讲解到这里就结束了,如果对你有用的话还请留下你的三连加关注。算法的学习还是建立在多练习上,学会了思想还需要多多实践才能熟练地掌握。

文章知识点与官方知识档案匹配,可进一步学习相关知识
算法技能树首页概览40346 人正在系统学习中