第 5 题:介绍下深度优先遍历和广度优先遍历,如何实现?
第五题问的是深度优先遍历和广度优先遍历,我是从 dom 节点的遍历来理解这个问题的。
我将用深度优先遍历和广度优先遍历对这个 dom 树进行查找。
深度优先遍历
深度优先遍历 DFS 与树的先序遍历比较类似。
假设初始状态是图中所有顶点均未被访问,则从某个顶点v出发,首先访问该顶点然后依次从它的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和 v 有路径相通的顶点都被访问到。若此时尚有其他顶点未被访问到,则另选一个未被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。
/*深度优先遍历三种方式*/
let deepTraversal1 = (node, nodeList = []) => {
if (node !== null) {
nodeList.push(node)
let children = node.children
for (let i = 0; i < children.length; i++) {
deepTraversal1(children[i], nodeList)
}
}
return nodeList
}
let deepTraversal2 = (node) => {
let nodes = []
if (node !== null) {
nodes.push(node)
let children = node.children
for (let i = 0; i < children.length; i++) {
nodes = nodes.concat(deepTraversal2(children[i]))
}
}
return nodes
}
// 非递归
let deepTraversal3 = (node) => {
let stack = []
let nodes = []
if (node) {
// 推入当前处理的node
stack.push(node)
while (stack.length) {
let item = stack.pop()
let children = item.children
nodes.push(item)
// node = [] stack = [parent]
// node = [parent] stack = [child3,child2,child1]
// node = [parent, child1] stack = [child3,child2,child1-2,child1-1]
// node = [parent, child1-1] stack = [child3,child2,child1-2]
for (let i = children.length - 1; i >= 0; i--) {
stack.push(children[i])
}
}
}
return nodes
}
输出结果
广度优先遍历
广度优先遍历 BFS
从图中某顶点 v 出发,在访问了 v 之后依次访问 v 的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使得 先被访问的顶点的邻接点先于后被访问的顶点的邻接点被访问,直至图中所有已被访问的顶点的邻接点都被访问到。 如果此时图中尚有顶点未被访问,则需要另选一个未曾被访问过的顶点作为新的起始点,重复上述过程,直至图中所有顶点都被访问到为止。
let widthTraversal2 = (node) => {
let nodes = []
let stack = []
if (node) {
stack.push(node)
while (stack.length) {
let item = stack.shift()
let children = item.children
nodes.push(item)
// 队列,先进先出
// nodes = [] stack = [parent]
// nodes = [parent] stack = [child1,child2,child3]
// nodes = [parent, child1] stack = [child2,child3,child1-1,child1-2]
// nodes = [parent,child1,child2]
for (let i = 0; i < children.length; i++) {
stack.push(children[i])
}
}
}
return nodes
}
输出结果
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(38)
感觉广度优先遍历使用的stack 应该使用queue 更合适吧 就是先push进去的 先shift()取出来 放在nodes里
所有的方法都缺少对 node.chilren的非空判断,如果对象没有children属性则会报错
深度优先遍历
遍历结果是: 1->2->4->8->5->3->6->7
广度优先遍历
遍历结果是:1->2->3->4->5->6->7->8
宽度优先遍历 和 深度优先遍历 是
遍历
或者搜索
图 和 树 的算法。深度优先遍历: 从根节点开始,沿着树的深度遍历树的节点,尽可能深的搜索树的分支。沿着一条可能的路径一直往下走,深入到不能深入为止。【可以纵向优先搜索】
宽度优先遍历: 从根节点开始,沿着树的宽度遍历树的节点。横向一层(level)的去遍历。
一楼大兄弟的html代码:
深度优先遍历 - 递归
深度优先遍历 - stack 先进后出 【push(item) -> pop】 或者 [unshift(item) -> shift()]
广度优先遍历 - queue 先进先出[push(item) -> shift()] 或者[unshift(item) -> pop()]
深度遍历
广度遍历
测试代码
我看大家广度优先都用两个数组做,其实用一个就可以了 ,利用for循环每次计算length的特性
广度优先遍历
`
`
我举个通俗的例子
假如你的面前摆了一排杯子,每个杯子里都装了不确定量的水。
深度优先遍历就是你拿起第一个杯子,喝光水,然后再拿起下一个杯子,喝光,一直到最后一个杯子。
广度优先遍历就是你从第一个杯子开始,每个杯子挨个都喝一口,然后又重头开始挨个喝一口,一直到全部喝完为止。
是这样吧?
遍历树型结构对象啊
第二个广度和第一个不是一样的嘛
没毛病的,循环是倒序。就是深度
获取项不一样 一个用到了pop 一个用shift
DFS 可以用在查找两个 dom 节点的相同祖先节点
数组扁平化
我在业务中有用到过:
1、富文本“一键排版”
2、树形组件,拖动排序,数据递归处理等
附上结果图:
这个是深度优先的,你看看它最后的循环,是从末尾开始,也就是说,再下一次循环进来的时候,每次出栈都是从后面最后一个开始[其实已经到了子节点的范畴了】,[(当前节点的子节点是被放在栈的后面)
深拷贝感觉是用到了深度优先, 广度优先,我还没写过...我得研究下
从掘金的广度深拷贝问题过来的,结果全是在聊遍历,自己试着实现了一下广度拷贝,不知道有没有更好的方式。
二叉树
广度优先遍历 BFS
思路是利用队列,从根节点开始,依次将左节点、右节点push进数组。
深度优先遍历 DFS
思路是利用栈,从根节点开始,依次将右、左节点入栈。
感谢楼主的回答,了解到了深度遍历和广度遍历区别,小白把代码放在codepen实现了一遍。https://codepen.io/valleylmh/pen/EBBrWg
深度优先:找到一个节点后,把它的后辈都找出来,最常用递归法。
广度优先:找到一个节点后,把他同级的兄弟节点都找出来放在前边,把孩子放到后边,最常用 while
为啥是广度的啊?deepTraversal3 不是把当前节点的所有子节点遍历出来之后,在遍历下一个同胞节点吗?
深度优先搜索以及广度优先搜索实现方式
问下大佬,有个旧的json 格式转成新的json格式
var old ={
"颜色":[
{
"黄色":{
"尺码":[
{
"XL":{
"形状":[
{
"多边形":1
},
{
"方形":2
}
]
}
},
{
"M":{
"形状":[
{
"方形":3
}
]
}
}
]
}
},
{
"红色":{
"尺码":[
{
"XL":{
"形状":[
{
"多边形":1
},
{
"方形":2
}
]
}
},
{
"M":{
"形状":[
{
"方形":3
}
]
}
}
]
}
}
]
}
var new =[
{
// priceId: 1,
// price: 35.0,
// "stock": 8,
"attrValueList": [
{
"attrKey": "颜色",
"attrValue": "黄色",
"attrCode": "1001"
},
{
"attrKey": "尺码",
"attrValue": "xm",
"attrCode": "2001"
},
{
"attrKey": "形状",
"attrValue": "多边形",
"attrCode": "3001"
}
]
有方便的转换方法吗
generator +Interator接口实现深度遍历
写得非常好了,我之前也了解过这两个东西,@atheist1 ,代码通俗易懂,答案简单直观,非常棒
这个字打错了吧
@atheist1 宽搜还是写 queue 吧,stack 歧义太严重了
这其实是和公司的具体业务相关的,我们公司把在业务层背后抽象了一套树形结构的领域对象模型,整个项目里少不了折腾树的递归,这时候发现熟练掌握深度遍历和广度遍历就十分有用了。
@caihg 我也是这样想的
@atheist1
非递归版的 deepTraversal3 实际上是广度优先的
前端确实很少涉及算法。 这个我也只在做小游戏的时候有用到过
图
图是一种复杂的非线性结构,它由边(边Edge)和点(顶点Vertex)组成。一条边连接的两个点称为相邻顶点。
图分为:
本文探讨的是无向图
图的表示
图的表示一般有以下两种:
arr[i][j] = 1
表示节点 i 与节点 j 之间有边,arr[i][j] = 0
表示节点 i 与节点 j 之间没有边创建图
下面声明图类,Vertex 用数组结构表示,Edge 用 map结构表示
测试:
测试成功
图的遍历
两种遍历算法:
深度优先遍历(DFS)
深度优先遍历(Depth-First-Search),是搜索算法的一种,它沿着树的深度遍历树的节点,尽可能深地搜索树的分支。当节点v的所有边都已被探寻过,将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已探寻源节点到其他所有节点为止,如果还有未被发现的节点,则选择其中一个未被发现的节点为源节点并重复以上操作,直到所有节点都被探寻完成。
简单的说,DFS就是从图中的一个节点开始追溯,直到最后一个节点,然后回溯,继续追溯下一条路径,直到到达所有的节点,如此往复,直到没有路径为止。
DFS 可以产生相应图的拓扑排序表,利用拓扑排序表可以解决很多问题,例如最大路径问题。一般用堆数据结构来辅助实现DFS算法。
注意:深度DFS属于盲目搜索,无法保证搜索到的路径为最短路径,也不是在搜索特定的路径,而是通过搜索来查看图中有哪些路径可以选择。
步骤:
实现:
测试:
测试成功
广度优先遍历(BFS)
广度优先遍历(Breadth-First-Search)是从根节点开始,沿着图的宽度遍历节点,如果所有节点均被访问过,则算法终止,BFS 同样属于盲目搜索,一般用队列数据结构来辅助实现BFS
BFS从一个节点开始,尝试访问尽可能靠近它的目标节点。本质上这种遍历在图上是逐层移动的,首先检查最靠近第一个节点的层,再逐渐向下移动到离起始节点最远的层
步骤:
实现:
测试:
测试成功
本文始发于我的博客:深度优先遍历与广度优先遍历
概念上我觉得没啥问题,具体在业务上有啥应用的地方吗(尤其是前端)?
2.14补充
对象的深拷贝