树遍历算法之深度优先和广度优先

2,956 阅读2分钟

前言

前两天看到一个阿里面试题 关于dfs 和bfs的算法题,在网上找了很多,没确切答案。今天我们一起来做下这个题目

题目:实现一个深度优先搜索算法(要求:不能用递归)

var tree = {
    name: '中国',
    children: [
        {
            name: '北京',
            children: [
                {
                    name: '朝阳群众',
                    children: [
                        {
                            name: '西宁市',
                            code: '0521',
                        }
                    ]
                },
                {
                    name: '海淀区',
                },
                {
                    name: '昌平区',
                },
            ],
        },
        {
            name: '浙江省',
            children: [
                {
                    name: '杭州市',
                    code: '0571',
                },
                {
                    name: '嘉兴市',
                },
                {
                    name: '绍兴市',
                },
                {
                    name: '宁波市',
                },
            ],
        },
    ],
};
var node = dfs/bfs(tree, '西宁市');
console.log(node); // 输出: { name: '西宁市', code: '0521' }

题目要求非递归方式拿到搜索内容,那么我们不用递归怎么去实现查找呢?

如果我们用递归方式非常简单:贴代码

// 递归算法
function dfs(tree, name){
    if(tree.name === name){
        return tree;
    }
    if(!tree.name && !tree.children){
        return null
    }
    for(let i = 0; i < tree.children.length; i++){
        var result = dfs(tree.children[i], name)
        if(result){
            return result;
        }
    }   
    return null
}

解答

方案一 :深度优先遍历

根据先进后出原则,我们从右即左,按深度方式便利 (找到终止循环break)。

/ 非递归算法 深度优先
function dfs(tree, name){
    var stack = [], result = {};
    stack.push(tree)
    while(stack.length != 0){
        var item = stack.pop();
        if(item.name == name){
            result = item;
            break
        }
        let children = item.children;
        if(children){
            for(let i = children.length - 1; i >= 0; i--){
                stack.push(children[i])
            }
        }
    }    return result
}

方案二 :广度优先遍历

根据先进先出原则,我们从上到下,按层级一层一层往下遍历 (找到终止循环break)

// 非递归算法 广度优先
function bfs(tree,name){
    var queue = [],result = {};;
    queue.unshift(tree);
    while(queue.length!=0){
        var item = queue.shift();
        if(item.name == name){
            result = item;
            break
        }
        var children=item.children;
        if(children){
            for(let i = 0; i < children.length; i++){
                queue.push(children[i])
            }
        }
    }    return result
}

深度优先算法占内存少但速度较慢,广度优先算法占内存多但速度较快,在距离和深度成正比的情况下能较快地求出最优解。

总结:

深搜优

  1. 能找出所有解决方案
  2. 优先搜索一棵子树,然后是另一棵,所以和广搜对比,有着内存需要相对较少的优点
  3. 要多次遍历,搜索所有可能路径,标识做了之后还要取消。
  4. 在深度很大的情况下效率不高

广搜优

  1.  对于解决最短或最少问题特别有效,而且寻找深度小

  2.  每个结点只访问一遍,结点总是以最短路径被访问,所以第二次路径确定不会比第一次短

  3.  内存耗费量大(需要开大量的数组单元用来存储状态)

 ~完 我是Steven,如果觉得还可以麻烦左上角赞一个,有问题可以评论或私信