递归-广度优先和深度优先

143 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第24天,点击查看活动详情

作为一个开发人员,难免会处理各种各样的数据。最常见的数据处理就是遍历和递归了。

最常见的遍历一般使用 for 循环或者 map/forEach 这些方法就可以实现,但是这些还不足以解决所有的场景。

本篇文章记录一下常见数据递归的两种常见场景:广度优先和深度优先。

数据准备

先准备一套树状结构数据,如下所示:


const data = [
  {
    id: 1,
    name: '0',
    children: [
      { id: 2, name: '0-1', children: [{ id: 6, name: '0-1-1' }] },
      { id: 7, name: '0-2', children: [{ id: 8, name: '0-2-1' }] },
    ],
  },
  {
    id: 3,
    name: '1',
    children: [{ id: 4, name: '1-1', children: [{ id: 5, name: '1-1-1' }] }],
  },
]

广度优先

广度优先是相对于深度优先来说的,简单理解一下就是,一层一层的遍历。

例如上面的数据,我们在根下有两个对象,首先参与遍历的就是 id 为 1 和 3 的两条数据,然后是 2/7/4 这样,从上到下一层一层的遍历,直到所有的 children 都照顾到,就算完成了一次遍历。

适用的场景按照一个地图顺序去查找一条数据,例如按照 1-2-6 的顺序去拿到 '0-1-1' 这条数据,我们就需要逐次的去匹配 id,然后拿到数据。

具体方法如下:

/**
 * 广度优先遍历方法
 * @param {an[]} tree 原始树状数据
 * @param {Function} func 回调函数
 * @returns 递归结果
 */
function treeForeach(tree, func) {
  let node /** 临时节点 */,
    list = [...tree] /** 展开运算符,展开节点 */,
    result = []; /** 最终返回结果 */

  // 遍历体
  while ((node = list.shift())) {
    // 执行回调方法
    // func(node)

    // 临时创建一个节点,保存在结果中
    let tmpNode = {
      id: node.id,
      name: node.name,
    };
    result.push(tmpNode);

    // 改变list,使得 while 条件进行下去
    node.children && list.push(...node.children);
  }
  // 返回结果
  return result;
}

const result = treeForeach(data);
console.log(result);

image.png

深度优先

如果说广度优先是一层一层的话,深度优先则是一个一个的来。

还是这套数据,我们会先从 id为1的数据开始,然后到 2 再到 6,完成第一个遍历,然后返回到 7-8 这样子去遍历数据。

使用场景的话,是比较适合去搜索第一个满足条件的数据,例如遍历权限,找到第一个 admin ,就可以退出遍历这样的场景。

贴出方法:

/**
 * 深度优先遍历
 * @param {object || any[]} node 递归数据
 * @param {any[]} nodeList 保存最终结果
 * @returns any[] nodeList
 */
const deepForeach = (node, nodeList = []) => {
  // 如果有node节点则执行主逻辑
  if (node) {
    // 导出结果填入节点数据
    nodeList.push({ id: node.id, name: node.name });
    // 如果有数据且有数据长度则继续递归
    node.children &&
      node.children.length &&
      node.children.forEach((item) => deepForeach(item, nodeList));
  }
  return nodeList;
};

console.log(data.map((item) => deepForeach(item)));

image.png

后记

上面这两个方法是看了一些前辈大佬们写的方法,又简单整理了一下才作为工具方法进行记录在这里的,其实只是核心思路。

不论是广度优先还是深度优先,其实都是我们解决实际开发问题的一种方案,实际工作中,还是会结合实际数据结构来选择采用的具体方案。

希望能对大家有些帮助。