热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

爱上算法,迷人的两度搜索,深度优先(DFS)和广度优先(BFS)

爱上,算法,迷人,的,两,度,搜索,深度,优先,dfs,和
迷人的两度搜索

1、BFS和DFS

深度优先搜索算法(DFS)和广度优先搜索算法(BFS)是一种用于遍历或搜索树或图的算法,在搜索遍历的过程中保证每个节点(顶点)访问一次且仅访问一次,按照节点(顶点)访问顺序的不同分为深度优先和广度优先。

1.1、深度优先搜索算法

深度优先搜索算法(Depth-First-Search,DFS)沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。属于盲目搜索。 file

注意:

1:实际上,回溯算法思想就是借助于深度优先搜索来实现的。

DFS负责搜索所有的路径,回溯辅以选择和撤销选择这种思想寻找可能的解,当然代码写起来基于递归(所以代码写起来就是用递归实现的)。

2:DFS跟回溯有什么关系呢?

回溯是一种通用的算法,把问题分步解决,在每一步都试验所有的可能,当发现已经找到一种方式或者目前这种方式不可能是结果的时候,退回上一步继续尝试其他可能(有一个选择和撤销选择的过程,可理解为标记访问和删除访问标记)。很多时候每一步的处理都是一致的,这时候用递归来实现就很自然。

当回溯(递归)用于树(图)的时候,就是深度优先搜索。当然了,几乎所有可以用回溯解决的问题都可以表示为树。(像之前的排列,组合等问题,虽不是直接在树上操作,但是他们操作的中间状态其实是一棵树)那么这俩在这里就几乎同义了。如果一个问题解决的时候显式地使用了树或图,那么我们就叫它dfs。很多时候没有用树我们也管它叫dfs严格地说是不对的,但是dfs比回溯打字的时候好输入。

DFS代码参考模板:

//Java private void dfs(TreeNode root,int level,List> results){ //terminal 已下探到最底部节点 if(results.size()==level){ // or root == null or node alread visited results.add(new ArrayList<>()); return; } // process current level node here. results.get(level).add(root.val); // 记录当前节点已被访问 //drill down if node not visited if(root.left!=null){ dfs(root.left,level+1,results); } if(root.right!=null){ dfs(root.right,level+1,results); } } 

是不是觉得跟二叉树的前中后序遍历很像,其实二叉树的前中后序遍历就是一种DFS,只不过记录节点的时机不一样而已。

针对多叉树的DFS,代码参考模板如下:

//Java public void dfs(Node node,List res) { //terminal if (node == null) { return; } //process current level logic res.add(node.val); //drill down List children = node.children; for (Node n:children) { // if node not visited then dfs node if (not visited) { // 在基于图的dfs中一般需要判断顶点是否已访问过 dfs(n,res); } } } 

当然我们也可以自己使用栈来模拟递归的过程,将递归代码改写成非递归代码!

针对图的深度优先搜索算法,思路是一致的:

file

假设:从S开始进行查找,每次查找,先一头扎到底,然后再回退,回退过程中有别的路再一头扎到底。比如:S->A->D->G->E->B,到底了,然后回退到G,再一头扎到底,S->A->D->G->F->C

1.2、广度优先搜索算法

广度优先搜索算法(Breadth-First-Search,BFS)直观地讲,它其实就是一种“地毯式”层层推进的搜索策略,即先查找离起始顶点最近的,然后是次近的,依次往外搜索。

简单的说,BFS是从根节点开始,沿着树(图)的宽度遍历树(图)的节点。如果所有节点均被访问,则算法中止,一般用队列数据结构来辅助实现BFS算法。

file

就像在湖面上滴一滴水,形成的水波纹!向四周散开

dfs和bfs搜索方式的比较:

file

BFS代码的参考模板:需要借助一个队列Queue(或Deque)

//Java public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } } public List> levelOrder(TreeNode root) { List> allResults = new ArrayList<>(); if (root == null) { return allResults; } Queue nodes = new LinkedList<>(); //将根节点入队列 nodes.add(root); while (!nodes.isEmpty()) { //每次循环开始时:队列中的元素的个数其实就是当前这一层节点的个数 int size = nodes.size(); List results = new ArrayList<>(); for (int i = 0; i 

就相当于刚开始把公司老总放入队列,这是第一层,然后把老总的直接下级比如:vp,总监等,取出来,然后放入队列,到了vp,总监这一层时,再把他们的直接下属放入队列。

在图中的广度优先搜索过程如下:

file

参考该网址上的演示过程:https://visualgo.net/zh/dfsbfs

应用特点:

1:BFS适合在树或图中求解最近,最短等相关问题

2:DFS适合在树或图中求解最远,最深等相关问题

3:实际应用中基于图的应用居多

2、面试实战

102. 二叉树的层序遍历

滴滴,美团点评,腾讯最近面试题,102. 二叉树的层序遍历

典型的BFS,借助队列FIFO特性,

class Solution { public List> levelOrder(TreeNode root) { //特殊判断 if (root == null) { return new ArrayList(); } Queue queue = new LinkedList(); queue.offer(root); List> res = new ArrayList(); while (!queue.isEmpty()) { int size = queue.size(); List list = new ArrayList(); for (int i=0;i

时间复杂度O(n),空间复杂度O(n)

进阶:能否基于DFS完成

思路:按照深度优先遍历,遍历过程中将当前节点的值添加到它所对应的深度的集合中。因此需要一个在dfs过程中代表深度的变量

class Solution { public List> levelOrder(TreeNode root) { List> res = new ArrayList(); dfs(root,0,res); return res; } public void dfs(TreeNode root,int level,List> res) { //terminal if (root==null) { return; } //将当前层的集合初始化好 int size = res.size(); if ( level > size-1) { res.add(new ArrayList()); } //将当前节点加到当前层对应的集合中 List list = res.get(level); list.add(root.val); //drill down dfs(root.left,level+1,res); dfs(root.right,level+1,res); } } 

104. 二叉树的最大深度

嘀嘀打车,百度最近面试题,104. 二叉树的最大深度

如果我们知道了左子树和右子树的最大深度 l 和 r,那么该二叉树的最大深度即为

max(l,r)+1 

而左子树和右子树的最大深度又可以以同样的方式进行计算。因此使用递归

其实这也是DFS的体现,直接下探到最深处得到最大深度,然后左右两边比较即可。

class Solution { public int maxDepth(TreeNode root) { return dfs(root); } //求一棵子树的最大深度 public int dfs(TreeNode node) { //终止条件 if (node == null) { return 0; } //左子树最大深度 int leftDepth = dfs(node.left); //右子树最大深度 int rightDepth = dfs(node.right); //当前节点的最大深度为左子树最大深度和右子树最大深度中的最大值+1 return Math.max(leftDepth,rightDepth) +1; } } 

时间复杂度:O(n),其中 n 为二叉树节点的个数。每个节点在递归中只被遍历一次。

空间复杂度:O(height),其中height 表示二叉树的高度。递归函数需要栈空间,而栈空间取决于递归的深度,因此空间复杂度等价于二叉树的高度。

进阶:能否用BFS完成

利用一个计数器,每遍历完一层就计一个数,直到所有层都遍历结束

class Solution { public int maxDepth(TreeNode root) { //特殊判断 if (root==null) { return 0; } Queue queue = new LinkedList(); queue.add(root); int deep = 0; while (!queue.isEmpty()) { int size = queue.size(); for (int i=0;i

小结:

在实际应用中,DFS要比BFS应用的广泛!

515. 在每个树行中找最大值

facebook,百度,字节面试题,515. 在每个树行中找最大值

典型的BFS

class Solution { public List largestValues(TreeNode root) { List res = new ArrayList(); if (root==null) { return res; } Queue queue = new LinkedList(); queue.offer(root); while (!queue.isEmpty()) { int size = queue.size(); int max = Integer.MIN_VALUE; for (int i=0;i

200. 岛屿数量

亚马逊,华为,字节最近面试题,很高频,200. 岛屿数量

典型的图的搜索,立马想到DFS和BFS

class Solution { //定义顶点向:上下左右,各走一步的方向信息 int[][] directiOns= {{0,1},{0,-1},{-1,0},{1,0}}; //定义网格的行数 int rows; //定义网格的列数 int clos; public int numIslands(char[][] grid) { //定义岛屿总数 int count = 0; //获取网格有多少行 rows = grid.length; if (rows==0) { return count; } //获取网格有多少列 clos = grid[0].length; //定义网格各顶点是否被访问过 boolean[][] visited = new boolean[rows][clos]; //从图中去找能构成岛屿的顶点 for (int i=0;i=0 && x=0 && y

其他题目

127. 单词接龙

529. 扫雷游戏

36. 有效的数独

本文由传智教育博学谷教研团队发布。

如果本文对您有帮助,欢迎关注点赞;如果您有任何建议也可留言评论私信,您的支持是我坚持创作的动力。

转载请注明出处!


推荐阅读
  • 在机器学习领域,深入探讨了概率论与数理统计的基础知识,特别是这些理论在数据挖掘中的应用。文章重点分析了偏差(Bias)与方差(Variance)之间的平衡问题,强调了方差反映了不同训练模型之间的差异,例如在K折交叉验证中,不同模型之间的性能差异显著。此外,还讨论了如何通过优化模型选择和参数调整来有效控制这一平衡,以提高模型的泛化能力。 ... [详细]
  • 兆芯X86 CPU架构的演进与现状(国产CPU系列)
    本文详细介绍了兆芯X86 CPU架构的发展历程,从公司成立背景到关键技术授权,再到具体芯片架构的演进,全面解析了兆芯在国产CPU领域的贡献与挑战。 ... [详细]
  • 【线段树】  本质是二叉树,每个节点表示一个区间[L,R],设m(R-L+1)2(该处结果向下取整)左孩子区间为[L,m],右孩子区间为[m ... [详细]
  • 本文详细介绍了 Java 网站开发的相关资源和步骤,包括常用网站、开发环境和框架选择。 ... [详细]
  • 本文节选自《NLTK基础教程——用NLTK和Python库构建机器学习应用》一书的第1章第1.2节,作者Nitin Hardeniya。本文将带领读者快速了解Python的基础知识,为后续的机器学习应用打下坚实的基础。 ... [详细]
  • 本文介绍了几种常用的图像相似度对比方法,包括直方图方法、图像模板匹配、PSNR峰值信噪比、SSIM结构相似性和感知哈希算法。每种方法都有其优缺点,适用于不同的应用场景。 ... [详细]
  • 本文介绍如何使用线段树解决洛谷 P1531 我讨厌它问题,重点在于单点更新和区间查询最大值。 ... [详细]
  • 本文对比了杜甫《喜晴》的两种英文翻译版本:a. Pleased with Sunny Weather 和 b. Rejoicing in Clearing Weather。a 版由 alexcwlin 翻译并经 Adam Lam 编辑,b 版则由哈佛大学的宇文所安教授 (Prof. Stephen Owen) 翻译。 ... [详细]
  • 解决Bootstrap DataTable Ajax请求重复问题
    在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
  • 全面升级的中文PubMed——Medreading
    Medreading 是一款由科研者之家(HOME for Researchers)推出的中文版PubMed,提供强大的文献检索和分析功能,支持AI辅助全文下载。 ... [详细]
  • 大类|电阻器_使用Requests、Etree、BeautifulSoup、Pandas和Path库进行数据抓取与处理 | 将指定区域内容保存为HTML和Excel格式
    大类|电阻器_使用Requests、Etree、BeautifulSoup、Pandas和Path库进行数据抓取与处理 | 将指定区域内容保存为HTML和Excel格式 ... [详细]
  • 题目《BZOJ2654: Tree》的时间限制为30秒,内存限制为512MB。该问题通过结合二分查找和Kruskal算法,提供了一种高效的优化解决方案。具体而言,利用二分查找缩小解的范围,再通过Kruskal算法构建最小生成树,从而在复杂度上实现了显著的优化。此方法不仅提高了算法的效率,还确保了在大规模数据集上的稳定性能。 ... [详细]
  • 本文是Java并发编程系列的开篇之作,将详细解析Java 1.5及以上版本中提供的并发工具。文章假设读者已经具备同步和易失性关键字的基本知识,重点介绍信号量机制的内部工作原理及其在实际开发中的应用。 ... [详细]
  • 在尝试对 QQmlPropertyMap 类进行测试驱动开发时,发现其派生类中无法正常调用槽函数或 Q_INVOKABLE 方法。这可能是由于 QQmlPropertyMap 的内部实现机制导致的,需要进一步研究以找到解决方案。 ... [详细]
  • 在《Cocos2d-x学习笔记:基础概念解析与内存管理机制深入探讨》中,详细介绍了Cocos2d-x的基础概念,并深入分析了其内存管理机制。特别是针对Boost库引入的智能指针管理方法进行了详细的讲解,例如在处理鱼的运动过程中,可以通过编写自定义函数来动态计算角度变化,利用CallFunc回调机制实现高效的游戏逻辑控制。此外,文章还探讨了如何通过智能指针优化资源管理和避免内存泄漏,为开发者提供了实用的编程技巧和最佳实践。 ... [详细]
author-avatar
翔云飘雪9_694_492
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有