js基础 - 深度优先遍历

274 阅读3分钟

深度优先遍历就是从根节点开始不停深入的搜索方法,当发现这条路走不通的时候就会回退到上一个节点,如果上一个节点存在没有遍历的分支,便继续遍历若没有则继续回退。

我们常见的树形结构数据的遍历就需要使用深度优先遍历或广度优先遍历

使用递归实现深度优先遍历

递归的特性使递归非常适合成为深度优先遍历的实现方案

/**
 * 深度优先遍历 - 递归
 * @param {array} tree 树状结构数据
 * @param {function} callback 遍历每一项时的回调函数
 */
function dfsHandler(tree, callback) {
    for (let index in tree) {
        const _node = tree[index];
        // 执行遍历回调方法
        typeof callback === 'function' && callback(_node);
        // 存在子节点时递归调用
        node.children && node.children.length && dfsHandler(node.children, callback);
    }
}

使用while循环实现深度优先遍历

拷贝原始数据(遍历数据被污染),使用while遍历拷贝数据,遍历时将第一个元素从拷贝数据中移除,当被移除的元素存在子节点时,将子节点全部解构到拷贝数据的头部直至拷贝数据的长度为0时遍历结束

/**
 * 深度优先遍历 - while循环
 * @param {array} tree 树状结构数据
 * @param {function} callback 遍历每一项时的回调函数
 */
 function dfsHandler(tree, callback) {
     const treeBackup = [...tree];
     
     while(treeBackup.length) {
         const _node = treeBackup.shift();
         // 执行遍历回调方法
         typeof callback === 'function' && callback(_node);
         // 存在子节点时将子节点添加到待遍历数据的头部位置
         _node.children && _node.children.length && treeBackup.unshift(..._node.children);
     }
}

给定一个节点如何查找节点的路径信息

前端路由配置就是树状结构,而每个路由路径就是通过遍历树状结构生成,当然Vue Router已经帮我们做好了路径规划,我们不需要额外的操心路由路径问题。

另一个常见的业务场景是省市区,通常省市区都会对应一个区域代码或则唯一的id,某些时候我们只会保存最后一级的区域代码,但前端组件通常需要我们设置完整的区域代码(省市区)才能正常回显,那我们如果通过一个区域代码查找当前区域的完整上级区域代码呢?

使用递归实现节点路径查找

在上面的递归实现中,我们需要适当的终端递归来返回节点的路径信息

/**
 * 在树形结构的数据中查找指定叶子节点的路径信息
 * @param {array} tree 树状结构数据
 * @param {string | number} leafId 目标叶子结点id
 * @param {object} treeShape 额外的配置信息(树形结构形状), 用于标记叶子节点的入口字段以及叶子节点的id
 * @returns array 返回叶子节点所在的路径详细信息
 */
export function getNodePathFromTree(tree, leafId, treeShape = {}) {
    // prettier-ignore
    const _nodePath = [], _treeShape = Object.assign({ 
        idField: 'id', 
        childField: 'children' 
    }, treeShape);
    function handler(data) {
        for (let i = 0; i < data.length; i++) {
            if (data[i][_treeShape.childField]) {
                if (handler(data[i][_treeShape.childField])) {
                    _nodePath.push(data[i]);
                    return true;
                }
            }
            if (data[i][_treeShape.idField] === leafId) {
                _nodePath.push(data[i]);
                return true;
            }
        }
    }
    return handler(tree), _nodePath.reverse();
}