深度优先搜索_深度优先搜索的理解与实现

前言

深度优先搜索作为广度优先搜索的好基友,同样也是对图进行搜索的一种算法。善用这两种算法,可以解决我们业务中遇到的「树形结构遍历搜索」问题。

本文将以图文的形式,详细讲解深度优先搜索,将其与广度优先搜索进行对比,分析两种算法的差异,并用JavaScript将其实现,欢迎各位感兴趣的前端开发者阅读本文。

概念

深度优先搜索是一个对图进行搜索的算法。

深度优先搜索与广度优先搜索一样,都是从图的起点开始搜索直到到达目标结点,深度优先搜索会沿着一条路径不断往下搜索直到不能再继续为止,然后再折返,开始搜索下一条候补路径。

图解示例

如图所示,我们想找G结点在图中的位置。f6f494f75dd46c5e38e48991cc993e71.png

  • 将A结点直达的三个结点B、C、D设为下一步的候补顶点a475f109839524f5548ddeda51842637.png
  • 在候补顶点中选择最后成为候补的结点a88c16dad680d8d222f7875514a45cf8.png
  • 移动选中的顶点到Ba25dffc6a1f6b64db5264d1d7c013d71.png
  • 将可以从B直达的两个结点E和F设为候补顶点b3ebe191e699ac93d63c4f4596afe5d5.png
  • 此时最后成为B的直达结点的是E,我们将当前点移动至E28620a04b78704069c34cb4a15bc0b28.png
  • 将可从E直达的顶点K设为候补顶点1cfc7fca844074775807947ad1287900.png
  • 重复上述操作直到到达终点,或者所有结点都被遍历为止
  • 此时结点到达了F,从A到F的搜索顺序为
A -> B -> E -> K
B -> F
85885e4fab48b4347985ae6c48396369.png
82c0b4231154b9a2db65e30ca29acaa9.png
  • 此时搜索到了结点C088e112d070311f78199cf409a3c616c.png
  • 到达终点G,搜索结束769f82420982b96c6ced8a707e90ac33.png

用JS实现深度优先搜索

正如图解示例所示,深度优先搜索会先将当前结点的直接子结点作为候选结点,挑选出最后加入的子结点,顺着挑选出来的结点一直往下找,直至没有子结点,返回上一级继续寻找其候选结点,直至所有结点都被找完或者取出的结点等于目标结点则搜索结束。

操作候选结点时,我们采用的是最后加入的子结点,也就是后入先出,满足了栈这种数据结构,因此可以「栈」来管理候选顶点。

  • 声明一个函数,参数为:需要查找的树,需要查找的结点
  • 用数组模拟栈,将要查找的树放入栈中
  • 遍历栈,直至栈中的数据为空
  • 取出栈顶元素,判断其是否有子结点
  • 如果存在子结点,遍历子结点将其放入栈中,遍历时需要确保它是从左到右遍历
  • 判断当前栈顶的元素是否与要查找的元素相等,如果相等则返回当前元素
  • 栈中数据全部遍历后,还是没找到目标结点,则证明目标结点不在树中,返回false。

接下来,我们将上述思路转换为代码:

/**
 * 深度优先搜索
 * @param tree 需要查找的树
 * @param target 需要查找的结点
 * @returns {{children}|*|undefined|boolean}
 */
function depthFirstSearch (tree, target) {
    // 用数组模拟栈,将树放进栈中
    let stack = [tree];
    while(stack.length!==0){
        // 取出数组的最后一个元素(栈顶)
        const stackTop = stack.pop();
        // 判断当前栈是否有子结点
        if (stackTop.children && stackTop.children.length) {
            /**
             * 将子结点入栈:
             *  1. 使用扩展运算符取出参数对象,使用reverse方法将数组中的元素进行颠倒
             *  2. 使用扩展运算符取出颠倒后数组中的对象
             *  3. 将取出的对象放进栈中
             */
            stack.push(...[...stackTop.children].reverse());
        }
        // 判断当前栈顶的元素是否为目标值
        if (stackTop.value === target) {
            return stackTop;
        }
    }
    return false;
}

接下来,我们用上一篇文章:广度优先搜索的理解与实现中的例子,来测试下我们编写的广度优先搜索函数

const targetVal = depthFirstSearch(dataTree,"天河区");
if(targetVal !==false){
    console.log(`目标结点的数据: `);
    console.log(targetVal);
}else{
    console.log(`目标结点不存在`);
}
856cfb2c23711bf3817447c8b9956fca.png
执行结果.png

深度优先搜索与广度优先搜索的区别

对广度优先搜索不了解的开发者请移步 => 广度优先搜索的理解与实现

本质区别

深度优先搜索:沿着一条路径不断往下,进行深度搜索。

广度优先搜索:顺着边不断进行搜索,直至找到目标结点。

候补顶点的选择

「广度优先搜索」选择的是最早成为候补的顶点,因为顶点离起点越近就越早成为候补,所以会从离起点近的地方开始按顺序搜索。

「深度优先搜索」选择的是最新成为候补的顶点,所以会一路往下,沿着新发现的路径不断深入搜索。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值