深度优先搜索和广度优先搜索,都是图形搜索算法,它两相似,又却不同,在应用上也被用到不同的地方。这里拿一起讨论,方便比较。
先给大家说一下两者大概的区别:

  1. 如果搜索是以接近起始状态的程序依次扩展状态的,叫广度优先搜索。
    如果扩展是首先扩展新产生的状态,则叫深度优先搜索。
    深度优先搜索:对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次。
    广度优先搜索:又叫层次遍历,从上往下对每一层依次访问,在每一层中,从左往右(也可以从右往左)访问结点,访问完一层就进入下一层,直到没有结点可以访问为止。

  2. 二叉树的深度优先遍历的非递归的通用做法是采用栈,广度优先遍历的非递归的通用做法是采用队列。

  3. 通常深度优先搜索法不全部保留结点,扩展完的结点从数据库中弹出删去,这样,一般在数据库中存储的结点数就是深度值,因此它占用空间较少。所以,当搜索树的结点较多,用其它方法易产生内存溢出时,深度优先搜索不失为一种有效的求解方法。  
    广度优先搜索算法,一般需存储产生的所有结点,占用的存储空间要比深度优先搜索大得多,因此,程序设计中,必须考虑溢出和节省内存空间的问题。但广度优先搜索法一般无回溯操作,即入栈和出栈的操作,所以运行速度比深度优先搜索要快些。

一、深度优先搜索

深度优先搜索属于图算法的一种,是一个针对图和树的遍历算法,英文缩写为DFS即Depth First Search。深度优先搜索是图论中的经典算法,利用深度优先搜索算法可以产生目标图的相应拓扑排序表,利用拓扑排序表可以方便的解决很多相关的图论问题,如最大路径问题等等。一般用栈stack数据结构来辅助实现DFS算法。其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。
若将bfs策略应用于树结构,其效果等同与前中后序遍历

1.1 基本步骤

  1. 对于下面的树而言,DFS方法首先从根节点1开始,其搜索节点顺序是1,2,3,4,5,6,7,8(假定左分枝和右分枝中优先选择左分枝)。
  2. 从stack中访问栈顶的点;
  3. 找出与此点邻接的且尚未遍历的点,进行标记,然后放入stack中,依次进行;
  4. 如果此点没有尚未遍历的邻接点,则将此点从stack中弹出,再按照(3)依次进行;
  5. 直到遍历完整个树,stack里的元素都将弹出,最后栈为空,DFS遍历完成。

1.2 代码模板

  1. 确认递归函数,参数
    首先我们dfs函数一定要存一个图,用来遍历的,还要存一个目前我们遍历的节点,定义为x。至于单一路径,和路径集合可以放在全局变量,那么代码是这样的:
vector<vector<int>> result; // 收集符合条件的路径
vector<int> path; // 0节点到终点的路径
// x:目前遍历的节点
// graph:存当前的图
void dfs (vector<vector<int>>& graph, int x)
  1. 确认终止条件
    什么时候我们就找到一条路径了?
    当目前遍历的节点 为 最后一个节点的时候,就找到了一条,从 出发点到终止点的路径。
    当前遍历的节点,我们定义为x,最后一点节点,就是 graph.size() - 1。
    所以 但 x 等于 graph.size() - 1 的时候就找到一条有效路径。 代码如下:
// 要求从节点 0 到节点 n-1 的路径并输出,所以是 graph.size() - 1
if (x == graph.size() - 1) { // 找到符合条件的一条路径result.push_back(path); // 收集有效路径return;
}
  1. 处理目前搜索节点出发的路径
    接下来是走 当前遍历节点x的下一个节点。
    首先是要找到 x节点链接了哪些节点呢? 遍历方式是这样的:
for (int i = 0; i < graph[x].size(); i++) { // 遍历节点n链接的所有节点

接下来就是将 选中的x所连接的节点,加入到 单一路径来。

path.push_back(graph[x][i]); // 遍历到的节点加入到路径中来
  1. 当前遍历的节点就是 graph[x][i] 了,所以进入下一层递归
dfs(graph, graph[x][i]); // 进入下一层递归
  1. 最后就是回溯的过程,撤销本次添加节点的操作。 该过程代码:
for (int i = 0; i < graph[x].size(); i++) { // 遍历节点n链接的所有节点path.push_back(graph[x][i]); // 遍历到的节点加入到路径中来dfs(graph, graph[x][i]); // 进入下一层递归path.pop_back(); // 回溯,撤销本节点
}
  1. 整体代码
// 深度优先搜索 C++代码模板
class Solution {private:vector<vector<int>> result; // 收集符合条件的路径vector<int> path; // 0节点到终点的路径// x:目前遍历的节点// graph:存当前的图void dfs (vector<vector<int>>& graph, int x, vector<bool>& used) {// 要求从节点 0 到节点 n-1 的路径并输出,所以是 graph.size() - 1if (x == graph.size() - 1) { // 找到符合条件的一条路径result.push_back(path);return;}unordered_set<int> visited; // 定义set对同一节点下的本层去重for (int i = 0; i < graph[x].size(); i++) { // 遍历节点n链接的所有节点// 要对同一树层使用过的元素进行跳过if (visited.find(graph[x][i]) != visited.end()) { // 如果发现出现过就passcontinue;}visited.insert(graph[x][i]);  //set跟新元素path.push_back(graph[x][i]); // 遍历到的节点加入到路径中来dfs(graph, graph[x][i]); // 进入下一层递归path.pop_back(); // 回溯,撤销本节点}}
public:vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph) {path.push_back(0); // 无论什么路径已经是从0节点出发dfs(graph, 0); // 开始遍历return result;}
};

二、广度优先搜索

广度优先搜索(也称宽度优先搜索,缩写BFS即即Breadth First Search)是连通图的一种遍历算法。这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和广度优先搜索类似的思想。其属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。基本过程,BFS是从根节点开始,沿着树(图)的宽度遍历树(图)的节点。

所采用的策略可概况为越早被访问到的顶点,其邻居顶点越早被访问。于是,从根顶点s的BFS搜索,将首先访问顶点s;再依次访问s所有尚未访问到的邻居;再按后者被访问的先后次序,逐个访问它们的邻居。一般用队列queue数据结构来辅助实现BFS算法。
若将bfs策略应用于树结构,其效果等同与层次遍历

2.1 基本步骤

仿照树的层次遍历,借助队列来存储已访问过得顶点。
过程:先从图中选取一个顶点,作为遍历图的起始顶点,并加入队列中,然后依次访问该顶点的邻居顶点,并按照顺序加入队列中。 当访问了该顶点的所有邻居顶点后,把该顶点从队列中移除。接着获取新的队列头,访问该顶点的邻居顶点,最后以此类推,直到所有顶点被访问。
在遍历时,该顶点的邻居顶点有可能已经被访问过了,意味着边不属于遍历树,可将该边归类为跨边(cross edge)

下图给出一个8个顶点,11条边的有向图的BFS,始于顶点s

2.2 代码模板

void bfs() {if(root==nullptr) return;queue<TreeNode*> que;queue.push(root);unordered_set<int> visited; // 记录遍历过的节点vector<vector<int>> result;//开始遍历队列while (!que.empty()) {int size = que.size();vector<int> vec;// 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的for (int i = 0; i < size; i++) {TreeNode* node = que.front();que.pop();if (visited.find(node) != visited.end()) { // 如果发现出现过就passcontinue;}visited.insert(node);   //set跟新元素vec.push_back(node->val);if (node->left) que.push(node->left);if (node->right) que.push(node->right);}result.push_back(vec);}
}

三、例题

岛屿的最大面积

3.1 深搜+递归

class Solution {public:// DFS 用递归实现int dfs(vector<vector<int>>& grid, vector<vector<bool>>& visited, int i, int j) {// 终止条件if(i<0 || j<0 || i>=grid.size() || j>=grid[0].size() || grid[i][j]==0 || visited[i][j]==true)return 0;visited[i][j]=true; // true为已遍历过int area = 1;vector<vector<int>> directions = {{1,0},{0,1},{-1,0},{0,-1}};for(auto dir : directions) {int next_i = i + dir[0];int next_j = j + dir[1];area += dfs(grid, visited, next_i, next_j);}return area;}int maxAreaOfIsland(vector<vector<int>>& grid) {vector<vector<bool>> visited(grid.size(), vector<bool>(grid[0].size(), false)); //初始化全为false没被遍历过int maxArea = 0;for(int i=0; i<grid.size(); i++) {for(int j=0; j<grid[0].size(); j++) {if(grid[i][j]==1 && visited[i][j]==false)maxArea = max(maxArea, dfs(grid, visited, i, j));}}return maxArea;}
};

3.2 广搜+迭代

class Solution {public:// BFS 用迭代实现int dfs(vector<vector<int>>& grid, vector<vector<bool>>& visited, int i, int j) {queue<pair<int, int>> que;que.push({i, j});visited[i][j]=true; // true为已遍历过int area = 0;while(!que.empty()) {auto pos = que.front();que.pop();area++;vector<vector<int>> directions = {{1,0},{0,1},{-1,0},{0,-1}};for(auto dir : directions) {int next_i = pos.first + dir[0];int next_j = pos.second + dir[1];if(next_i>=0 && next_i<grid.size() && next_j>=0 && next_j<grid[0].size() && grid[next_i][next_j]==1 && visited[next_i][next_j]==false) {que.push({next_i, next_j});visited[next_i][next_j] = true;}                   }}return area;}int maxAreaOfIsland(vector<vector<int>>& grid) {vector<vector<bool>> visited(grid.size(), vector<bool>(grid[0].size(), false)); //初始化全为false没被遍历过int maxArea = 0;for(int i=0; i<grid.size(); i++) {for(int j=0; j<grid[0].size(); j++) {if(grid[i][j]==1 && visited[i][j]==false)maxArea = max(maxArea, dfs(grid, visited, i, j));}}return maxArea;}
};

3.3 深搜+迭代

class Solution {public:int maxAreaOfIsland(vector<vector<int>>& grid) {int ans = 0;for (int i = 0; i != grid.size(); ++i) {for (int j = 0; j != grid[0].size(); ++j) {int cur = 0;stack<int> stacki;stack<int> stackj;stacki.push(i);stackj.push(j);while (!stacki.empty()) {int cur_i = stacki.top(), cur_j = stackj.top();stacki.pop();stackj.pop();if (cur_i < 0 || cur_j < 0 || cur_i == grid.size() || cur_j == grid[0].size() || grid[cur_i][cur_j] != 1) {continue;}++cur;grid[cur_i][cur_j] = 0;int di[4] = {0, 0, 1, -1};int dj[4] = {1, -1, 0, 0};for (int index = 0; index != 4; ++index) {int next_i = cur_i + di[index], next_j = cur_j + dj[index];stacki.push(next_i);stackj.push(next_j);}}ans = max(ans, cur);}}return ans;}
};

深度优先搜索(DFS)和广度优先搜索(BFS)相关推荐

  1. 数据结构与算法(7-2)图的遍历(深度优先遍历DFS、广度优先遍历BFS)(分别用邻接矩阵和邻接表实现)

    目录 深度优先遍历(DFS)和广度优先遍历(BFS)原理 1.自己的原理图 2.官方原理图 一.邻接矩阵的深度优先遍历(DFS) 1.原理图 2. 过程: 3.总代码 二.邻接表的深度优先遍历(DFS ...

  2. 一文搞定深度优先搜索(DFS)与广度优先搜索(BFS)【含完整源码】

    写在前面:博主是一位普普通通的19届双非软工在读生,平时最大的爱好就是听听歌,逛逛B站.博主很喜欢的一句话花开堪折直须折,莫待无花空折枝:博主的理解是头一次为人,就应该做自己想做的事,做自己不后悔的事 ...

  3. 图的深度优先搜索(DFS)和广度优先搜索(BFS)算法

    深度优先(DFS) 深度优先遍历,从初始访问结点出发,我们知道初始访问结点可能有多个邻接结点,深度优先遍历的策略就是首先访问第一个邻接结点,然后再以这个被访问的邻接结点作为初始结点,访问它的第一个邻接 ...

  4. 【蓝桥杯】2015决赛A组 5 穿越雷区(深度优先搜索dfs、广度优先搜索bfs)

    历届试题 穿越雷区 问题描述 X星的坦克战车很奇怪,它必须交替地穿越正能量辐射区和负能量辐射区才能保持正常运转,否则将报废. 某坦克需要从A区到B区去(A,B区本身是安全区,没有正能量或负能量特征), ...

  5. 树的前序、中序、后序遍历及深度优先算法DFS、广度优先算法BFS及python实现

    刷Leetcode时遇到一种经典数据结构--树,树是典型递归思想来的,学习树有助于学习递归思想以及栈.队列(后续细说),本文对树的结构.遍历算法以及Python实现做总结,以供复习拓展 树是连通的无环 ...

  6. 【数据结构与算法】2.深度优先搜索DFS、广度优先搜索BFS

    原文链接:https://blog.csdn.net/qq_41681241/article/details/81432634 总结 一般来说,广搜常用于找单一的最短路线,或者是规模小的路径搜索,它的 ...

  7. 深度优先搜索(DFS)和广度优先搜索(BFS)探究

    附BFS解题代码: #include<iostream> #include<queue> using namespace std;char room[23][23]; int ...

  8. 图的遍历(深度优先遍历DFS,广度优先遍历BFS)以及C语言的实现

    遍历的定义: 从已给的连通图中某一顶点出发,沿着一些边访遍图中所有的顶点,且使每个顶点仅被访问一次,就叫做图的遍历,它是图的基本运算. 一:深度优先遍历(DFS) 1,在访问图中某一起始顶点V后,由V ...

  9. 数据结构-图的深度优先遍历(DFS)和广度优先遍历(BFS)算法分析

    https://www.cnblogs.com/qzhc/p/10291430.html 最后一个广度优先有错误,H不指向E,只有G指向E,所以顺序应该是ABCFDHGE

  10. 深度优先搜索遍历与广度优先搜索遍历

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 深度优先 ...

最新文章

  1. rhel6多台主机的HA集群,并实现增加仲裁盘和共享存储
  2. count/distinct/group by的用法总结
  3. python爬虫反爬 对方是如何丧心病狂的通过css_如何应对网站反爬虫策略?如何高效地爬大量数据?...
  4. pip 安装模块报错解决
  5. 定时器取数据时实时进来的数据_Redis-数据淘汰策略amp;持久化方式(RDB/AOF)amp;Redis与Memcached区别...
  6. 关于node.js和npm 和nvm_byKL
  7. 以需求管理为例,产品经理如何打造自己的需求分析能力
  8. 不会用Photoshop抠图?Python助你一键“除”人!
  9. Windows文件服务器会话清理,在 Windows Server 上使用磁盘清理
  10. 找寻创业赛事活动有哪些靠谱的创业赛事平台?
  11. maven编译时出现读取XXX时出错invalid LOC header bad signature
  12. Windows文件服务器搭建
  13. Ubuntu修改DNS
  14. 必看!软考系统架构设计师考试详情与备考攻略
  15. 撩妹代码html,Web前端
  16. 餐饮SaaS行进时:美团To B,二维火To C
  17. dlink中设置端口映射图文讲解(解决电驴tcp链接测试失败问题)
  18. 在北京工作了两年,现在跳槽到了广州,社保公积金该如何办理?
  19. 检测计算机故障五大原则,电脑故障维修的基本原则及流程
  20. 女程序员职业发展的特别之处

热门文章

  1. 实训第 3 天 ------ 7.17
  2. 使用requests和re模块爬取i春秋论坛的精品贴(小爬虫)
  3. gitHub创建分支命令操作
  4. Windows 10 更新到 Windows 11
  5. WLAN-Web配置AP
  6. JS 实现替换字符串中所有指定字符总结
  7. 深入浅出理解kNN(k近邻算法)
  8. tinymce php,tinymce用php改变
  9. 广义pareto分布_狭义Pareto分布.pdf
  10. C++值传递和引用传递