当前位置:   article > 正文

深度优先搜索及讲解【新手易懂】

深度优先搜索及讲解【新手易懂】

一、深度优先搜索概念

DFS(Depth-First Search):深度优先搜索属于图算法的一种,其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。

深度优先遍历图的方法是,从图中某顶点v出发:

  1. 访问顶点v;
  2. 依次从v的未被访问的邻接点出发,对图进行DFS;直至图中和v有路径相通的顶点都被访问;
  3. 若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行DFS,直到图中所有顶点均被访问过为止。

深度优先遍历使用的数据结构是栈(Stack),将访问过的节点标记后,并压入栈中,再遍历此时跟栈顶元素相关联的节点,将其中未标记的节点标记,并压入栈中……以此类推,当该栈顶的元素相关联的节点都被访问过了,则该元素弹出栈……直到栈空,遍历完成。

二、深度优先搜索算法举例

  1. 给定一个无向图graph,当这个图为二分图时返回true
  2. 如果我们能将一个图的节点集合分割成两个独立的子集A和B,并使图中的每一条边的两个节点一个来自A集合,一个来自B集合,我们就将这个图称为二分图。
  3. graph将会以邻接表方式给出,graph[i]表示图中与节点i相连的所有节点。每个节点都是一个在0到graph.length-1之间的整数。这图中没有自环和平行边:graph[i]中不存在i,并且graph[i]中没有重复的值。
  4. 示例 1:
  5. 输入: [[1,3], [0,2], [1,3], [0,2]]
  6. 输出: true
  7. 解释:
  8. 无向图如下:
  9. 0----1
  10. | |
  11. | |
  12. 3----2
  13. 我们可以将节点分成两组: {0, 2} 和 {1, 3}。
  14. 示例 2:
  15. 输入: [[1,2,3], [0,2], [0,1,3], [0,2]]
  16. 输出: false
  17. 解释:
  18. 无向图如下:
  19. 0----1
  20. | \ |
  21. | \ |
  22. 3----2
  23. 我们不能将节点分割成两个独立的子集。

该题即利用栈,从某一个节点按顺序出发,如果未标记,则压栈并标记,栈非空时,则出栈并将关联的节点继续压栈,直到栈空为止,再下一个节点……

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<iomanip>
  4. using namespace std;
  5. int m,n,a[10010]={0},b[10010]={0};
  6. void d(int k)
  7. {
  8. for(int i=a[k-1];i<=m;i++)
  9. {
  10. if(b[i]==0)
  11. {
  12. b[i]=1; //赋值
  13. a[k]=i;
  14. if(k==n)
  15. {
  16. for(int j=1;j<=k;j++)
  17. {
  18. cout<<setw(3)<<a[j];
  19. }
  20. cout<<endl;
  21. }
  22. else d(k+1); //返回
  23. b[i]=0; //每当执行完都要为下一条做准备初始化
  24. a[k]=0;
  25. }
  26. }
  27. }
  28. int main()
  29. {
  30. cin>>m>>n;
  31. a[0]=1;
  32. d(1);
  33. return 0;
  34. }

上面这题通过自己手动创建栈来维护了深度遍历的结构,下面再看一题利用递归的方式来做的:

  1. 【题目描述】
  2. 一天Extense在森林里探险的时候不小心走入了一个迷宫,迷宫可以看成是由n×n的格点组成,每个格点只有2种状态,.和#,前者表示可以通行后者表示不能通行。同时当Extense处在某个格点时,他只能移动到东南西北(或者说上下左右)四个方向之一的相邻格点上,Extense想要从点A走到点B,问在不走出迷宫的情况下能不能办到。如果起点或者终点有一个不能通行(为#),则看成无法办到。
  3. 【输入】
  4. 1行是测试数据的组数k,后面跟着k组输入。每组测试数据的第1行是一个正整数n(1≤n≤100),表示迷宫的规模是n×n的。接下来是一个n×n的矩阵,矩阵中的元素为.或者#。再接下来一行是4个整数ha,la,hb,lb,描述A处在第ha行, 第la列,B处在第hb行, 第lb列。注意到ha,la,hb,lb全部是从0开始计数的。
  5. 【输出】
  6. k行,每行输出对应一个输入。能办到则输出“YES”,否则输出“NO”。
  7. 【输入样例】
  8. 2
  9. 3
  10. .##
  11. ..#
  12. #..
  13. 0 0 2 2
  14. 5
  15. .....
  16. ###.#
  17. ..#..
  18. ###..
  19. ...#.
  20. 0 0 4 0
  21. 【输出样例】
  22. YES
  23. NO

代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. using namespace std;
  4. int t,n,m1,n1,m2,n2,bz=0;
  5. char a[1010][1010];
  6. int dx[5]={0,0,0,1,-1}; //横坐标,只有四个方向,加上中心点
  7. int dy[5]={0,-1,1,0,0}; //纵坐标,只有四个方向,加上中心点
  8. void d(int x,int y)
  9. {
  10. if(bz==1) return; //省空间,只要能走到终点就直接停止函数
  11. for(int i=1;i<=4;i++)
  12. {
  13. if(x+dx[i]>=0&&x+dx[i]<n&&y+dy[i]>=0&&y+dy[i]<n&&a[x+dx[i]][y+dy[i]]=='.')//导航
  14. {
  15. a[x+dx[i]][y+dy[i]]='#'; //把之前走过的路口封住
  16. if(x+dx[i]==m2&&y+dy[i]==n2) //到达终点
  17. {
  18. bz=1;
  19. }
  20. else d(x+dx[i],y+dy[i]); //返回
  21. //a[x+dx[i]][y+dy[i]]='.';
  22. }
  23. }
  24. }
  25. int main()
  26. {
  27. cin>>t;
  28. for(int p=1;p<=t;p++)
  29. {
  30. cin>>n;
  31. for(int i=0;i<n;i++)
  32. {
  33. for(int j=0;j<n;j++)
  34. {
  35. cin>>a[i][j];
  36. }
  37. }
  38. cin>>m1>>n1>>m2>>n2;
  39. if(a[m1][n1]=='#'||a[m2][n2]=='#') //如果起点是障碍物,那就走不了,只能输出NO,然后继续
  40. {
  41. cout<<"NO";
  42. cout<<endl;
  43. continue; //continue就是继续执行
  44. }
  45. a[m1][n1]='#'; //起点可以走了,先初始化
  46. bz=0;
  47. d(m1,n1);
  48. if(bz==0)
  49. {
  50. cout<<"NO"<<endl;
  51. }
  52. else
  53. {
  54. cout<<"YES"<<endl;
  55. }
  56. }
  57. return 0;
  58. }

三、深度优先搜索算法公式

使用栈的公式:【转载】

  1. public boolean dfsStack(int[][] graph) {
  2. createFlagArray(); // 创建标记数组
  3. for (……){ // 按序遍历所有点
  4. if (!flag) { // 是否未标记
  5. createAndPushStack(); // 创栈并入栈,放入第一个启动值
  6. flag(); // 同时做好标记
  7. while (!stack.isEmpty()) { // 栈不空时一直循环
  8. nodes = findRelationNode(); // 栈顶弹出,并找栈顶元素的关联节点值
  9. for (node : nodes) { // 遍历关联节点
  10. if (!flag) { // 关联节点未标记
  11. pushStack(); // 关联节点入栈
  12. flag(); // 同时做好标记
  13. } else if (flag) { // 关联节点已标记
  14. return false; // 判断终止条件做处理
  15. }
  16. }
  17. }
  18. }
  19. }
  20. return true;
  21. }

使用递归的公式:

  1. public int dfsRecursion(char[][] grid) {
  2. for (……){ // 按序遍历所有点
  3. if (……){ // 额外的一些统计
  4. count++;
  5. }
  6. helper(i, j, grid); // 开始递归遍历,指定起点
  7. }
  8. return count; // 返回结果
  9. }
  10. private void helper(int row, int col, char[][] grid) {
  11. checkValid(); // 检查是否无效,或已标记(即终止条件)
  12. flag(); // 正常标记
  13. helper(row + 1, col, grid); // 递归遍历所有
  14. helper(row, col + 1, grid);
  15. helper(row - 1, col, grid);
  16. helper(row, col - 1, grid);
  17. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/492719
推荐阅读
相关标签
  

闽ICP备14008679号