教你彻底搞清楚BFS和DFS

附Java/C/C++/机器学习/算法与数据结构/前端/安卓/Python/程序员必读书籍书单大全:

书单导航页(点击右侧 极客侠栈 即可打开个人博客):极客侠栈
【Java】学习之路吐血整理技术书从入门到进阶最全50+本(珍藏版)
【算法数据结构+acm】从入门到进阶吐血整理书单50+本(珍藏版)
【数据库】从入门到进阶必读18本技术书籍网盘吐血整理网盘(珍藏版)
【Web前端】从HTML到JS到AJAX到HTTP从框架到全栈帮你走更少弯路(珍藏版)   
【python】书最全已整理好(从入门到进阶)(珍藏版)

【机器学习】+python整理技术书(从入门到进阶已经整理好)(珍藏版)
【C语言】推荐书籍从入门到进阶带你走上大牛之路(珍藏版)
【安卓】入门到进阶推荐书籍整理pdf书单整理(珍藏版)

【架构师】之路史诗级必读书单吐血整理四个维度系列80+本书(珍藏版)

【C++】吐血整理推荐书单从入门到进阶成神之路100+本(珍藏)

【ios】IOS书单从入门到进阶吐血整理(珍藏版)

-------------------------------------------------------------------------------------------------------------------------------------------

最近又有点学不进去了,不知道是不是天气热的缘故哈,没办法只好写一点算法来保持学习的路线不间断咯。 关于BFS和DFS,这是我们在面试的时候经常会遇到的两个基础算法,为什么说基础呢?因为它理解了之后才10行左右的代码,你说基础不基础?

一、 BFS

BFS,全称:Breadth First Search。中文翻译为广度优先搜索或者是宽度优先搜索,具体是怎么回事儿呢?
首先来用下面一颗的树来引入一下广度优先搜索的实现步骤:


如上图所示,我们先用一棵树来引入广度优先搜索。为什么要用树呢?因为我觉得树来入门是最简单的,也是最容易理解的。
首先按照广度优先搜索的方法,我们就应该就是这样来搜索:
A->B->C->D->E->F
或者是:
A->C->B->F->D->E

我先来从结果分析上面的遍历结果,我发现广度优先搜索有这样的特点对:

  • 先从根节点处查找,然后一层一层的往下查找。
    怎么理解呢?首先查找A也就是第一层,然后再查找BC,也就是第二层,最后查找DEF 也就是第三层。
  • 查找子层的时候,应该按照父层的顺序来查找子层。
    怎么理解呢?首先查找A节点,然后查找A的子层B和C,当然我们在查找A子层的时候先来查找的B节点,那么在查找B的子节点的时候就要优先查找B的子节点。同理如果第二层里面先查找C也是一样。
  • 广度优先搜索的搜索结果并不唯一。

通过上面的结果来分析,其实我们很容易发现广度优先算法有点队列的意味在里面。
怎么理解呢?来看下面一套图:

首先新建一个队列,先去查找一下树的根结点,并且将根结点A放入队列中。

移出节点A,并且把A的子节点加入到队列中。

移出节点B,并且把B的子节点加入到队列中。

依次类推,就将所谓的最后的广度优先搜索的路线给画出来了:
最终结果

通过前面的探究,我们大概晓得了广度优先搜索的一个大致流程,也差不多知道其实现的规则有点类似于队列。此时可能有人会说了,你只是讲解了一个简单的树,而我们看到的BFS一般是用图来展示的。
针对上面的疑问,我们首先来画一个无向图。

其实在我的理解里面:图和树最大的区别就是树有专门的起点,但是图却没有固定的起点。我们可以从任何一个点做起点来去搜索,例如:从A点出发,搜索结果是:
A->B->C->D->E->F
从E点出发:
E-> C->D->F->A->B

这样,我们其实就可以把之前树的那一套类似到图中,只不过图没有起点罢了,并且将树的子层换成了与指定节点相连的节点。这样类比之下也可以用队列来实现。具体的代码如下:

首先我们建立一张图:

let graph = {
	'A':['B','C'],
	'B':['A','C','D'],
	'C':['A','D','E'],
	'D':['B','C','E'],
	'E':['C','D','F'],
	'F':['E']
}

然后BFS的实现代码如下:

function bfs(graph,startPoint){
	let queue = [];
	let result = []

	queue.push(startPoint);
	result.push(startPoint);

	while(queue.length>0){
		let point = queue.shift();
		let nodes = graph[point];
		for(let node of nodes){
			if(result.includes(node)) continue;
			result.push(node);
			stack.push(node);
		}
	}
	return result

}

二、DFS

好了,前面谈到了广度优先搜索,那么什么是深度优先搜索呢?
首先我来用一套图集来辅助理解深度优先搜索的历程:


首先选定起点为A(起点是任意的),然后从A出发,搜索到B

然后再从B出发,搜索到C

再从C出发搜索到E

再从E出发搜索到D,此时整张图都没有与D相连但是没被搜索到的节点了,此时该怎么办呢?

如果搜索到没有可以搜索的节点,就应该回溯到最近存在相邻可以的节点处继续搜索。

总之,我对于深度优先搜索的理解就是:

  • 访问顶点A
  • 依次从A的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和A有路径相通的顶点都被访问;
  • 若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止。

这样看来,深度优先是不是有一点栈的意思在里面呢?怎么理解哦,我们再来看下面一套图。


首先我们从A出发,将节点A移入栈,然后将A移出栈

将A的子节点C,B移入栈内。

然后将B移出栈,并将与B相连的D,C节点移入栈内

然后将C移出栈,将与C相连的D,E节点移入栈内

然后将E移出栈,将与E相连的F,D节点移入栈内

然后将D移除栈,我们发现并没有可用的新节点了,就不再移入直接移出。

移出F节点

当我在移除新D节点的时候,发现D节点已经被移出过了。此时我们就将该节点丢弃,同样后面的节点也是一样。最后就完成了深度优先搜索。

通过上面的图级演示是不是很容易就能看出来深度优先搜索的实现原理呢?下面我们用代码的方式来去演示一下其原理吧:

function dfs(graph,startPoint){
	let stack = [];
	let result = []

	stack.push(startPoint);
	result.push(startPoint);

	while(stack.length>0){
		let point = stack.pop();
		let nodes = graph[point];
		for(let node of nodes){
			if(result.includes(node)) continue;
			result.push(node);
			stack.push(node);
		}
		

	}
	return result

}

说在最后

说了这么多,感觉午休的时间都所剩无几了,感觉自己还是没有把这部分的内容讲清楚。本来想额外写一点关于广度优先搜索的更深层次的用法的(作为很多图形结构的基础,其实应用还是比较广的),但我还是需要睡的呀!反正中午看来是说不完了。关于还没写完的内容呢,我这里提及一下:
如果相邻节点之间的距离相同的情况下,我们想求广度优先搜索的最短路径怎么来求呢?
如果相邻节点之间的距离不同呢?我们应该如何来计算呢
如果明天中午有机会的话,我会细细讲解这一块的内容的,敬请期待哦!
最后最后提到一句,欢迎大家点赞,关注我的个人博客,我会源源不断的输出高质量文章的。

<think>嗯,用户想学习BFSDFS,作为纯新手,我需要从基础开始解释。首先得明确这两个算法是什么,应用场景,然后步骤,可能还需要例子。用户可能不太懂图论,所以得先简单介绍图的概念。 BFSDFS都是图的遍历算法,但遍历方式不同。BFS是广度优先,一层层展开,可能用队列实现;DFS是深度优先,一条路走到底,用栈或递归。需要比较它们的区别,优缺点,适用情况。 用户是新手,可能需要代码示例,但可能不会编程,所以得用通俗的语言解释过程。比如用树或迷宫的例子来说明。比如走迷宫,BFS会尝试所有可能的路径,按层扩展;DFS会先沿着一条路走到底,再回溯。 还要注意避免术语太复杂,比如邻接表、邻接矩阵这些可以后面提,先讲思想。可能需要分步骤讲解,每一步做什么,数据结构怎么用。 另外,用户可能不知道什么时候用BFSDFS,需要举实际例子,比如最短路径用BFS,拓扑排序用DFS。同时要指出各自的优缺点,比如BFS空间消耗大,DFS可能陷入深层次。 可能需要画图辅助,但文本描述的话,可以用文字描述过程。比如BFS的队列操作,节点出队,邻居入队,标记已访问。DFS则是压栈,访问,直到没有未访问的邻居,再回溯。 还要注意检查用户可能的疑问,比如为什么BFS能找到最短路径,而DFS不一定。这时候需要解释因为BFS按层扩展,第一次到达就是最短的,而DFS可能绕远路。 最后总结对比,帮助用户记忆。可能还要提醒常见错误,比如忘记标记已访问导致循环。代码结构的话,伪代码可能更易懂,比如用队列的BFS循环,递归的DFS函数。 总之,结构应该是:介绍概念,应用场景,步骤详解,例子说明,比较区别,最后总结。确保语言简单,避免过多术语,多用类比。</think>好的,我将以新手友好的方式,分步骤为您讲解BFS(广度优先搜索)DFS(深度优先搜索)。这两个算法是处理树图结构的核心基础。 # 一、基础概念 - **BFS**:像水波扩散,逐层遍历 - **DFS**:像走迷宫,一条路走到底再回头 # 二、算法步骤详解 ## 1. BFS实现步骤 ``` 初始化队列,放入起点 标记起点已访问 while 队列不为空: 取出队首元素 访问该元素(如打印) 将其未访问的邻居加入队列 标记这些邻居为已访问 ``` 🌰 示例:二叉树的层序遍历 ```python def bfs(root): queue = [root] while queue: node = queue.pop(0) print(node.val) if node.left: queue.append(node.left) if node.right: queue.append(node.right) ``` ## 2. DFS实现步骤(递归版) ``` 标记当前节点已访问 访问当前节点 递归访问所有未访问的相邻节点 ``` 🌰 示例:二叉树的前序遍历 ```python def dfs(node): if not node: return print(node.val) # 访问在前 dfs(node.left) dfs(node.right) ``` # 三、关键对比表 | 特性 | BFS | DFS | |---------------|-------------------------|-------------------------| | 数据结构 | 队列(FIFO) | 栈(LIFO)或递归调用栈 | | 空间复杂度 | O(最宽层的节点数) | O(最大深度) | | 适用场景 | 最短路径、层序遍历 | 路径存在性、拓扑排序 | | 找到解的路径 | 最短路径 | 不一定最短 | # 四、实际应用场景 - **BFS典型应用**: - 社交网络中的三度好友推荐 - 迷宫最短路径(走迷宫最少步数) - 网页爬虫按层级抓取 - **DFS典型应用**: - 文件系统的目录遍历 - 数独求解 - 语法分析(如括号匹配) # 五、新手常见误区 1. **忘记标记已访问**:会导致无限循环 2. **DFS递归过深**:可能触发栈溢出(可改用迭代式DFS) 3. **错误的数据结构**:BFS用栈会变成DFSDFS用队列会变成BFS # 六、练习题建议 1. 实现二叉树层序遍历(BFS) 2. 判断图中两点是否连通(DFS/BFS) 3. 使用DFS计算二叉树的最大深度 建议从树结构开始练习,再过渡到图结构。理解核心思想后,您会发现这两个算法会频繁出现在各类算法问题中!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值