谁还不会树形结构遍历? 一文通晓

谁还不会树形结构遍历? 一文通晓

背景

昨天做了个需求, 关于element级联选择器的,后端要求只要叶子节点的数据,且只返回叶子节点的数据,需要前端遍历树取父级节点数据,来回显节点数据。(阿伟, 我不能直接给你么???????????)

案例结构

js 复制代码
let tree = [
    {
      id: 'a',
      children: [
            {
              id: 'b',
              children: [
                {
                  id: 'c',
                  children: null
                }
              ]
            },
            {
              id: 'd',
              children: [
                {
                  id: 'e',
                  children: null
                }
              ]
            },
            {
              id: 'f',
              children: [
                {
                  id: 'g',
                  children: null
                }
              ]
            }
      ]
    },
    {
      id: 'h',
      children: [
            {
              id: 'i',
              children: [
                {
                  id: 'j',
                  children: null
                }
              ]
            },
            {
              id: 'k',
              children: [
                {
                  id: 'l',
                  children: null
                }
              ]
            },
            {
              id: 'm',
              children: [
                {
                  id: 'n',
                  children: null
                }
              ]
            }
          ]

    }
   ]

输入/输出

级联选择器选择叶子节点后, 后台返回叶子节点数据处理成回显的数据.(多选)

输入 输出
['c','e'] [['a','b','c'],['a','d','e']]

树形遍历

深度递归遍历

  1. 递归遍历到目标节点
  2. 节点入栈
  3. 函数出栈, 节点入栈
  4. 直到函数作用结束
js 复制代码
findIdsByValue(root, id, stack) {
      if (!root) return;
      if (Array.isArray(root)) {
        for (const iterator of root) {
          if (iterator.id === id) {
            stack.unshift(iterator.id);
            return stack;
          }
          if (iterator.children && Array.isArray(iterator.children)) {
            const target = this.findIdsByValue(
              iterator.children,
              id,
              stack
            );
            if (target) {
              stack.unshift(iterator.id);
              return target;
            }
          }
        }
      }
    }

这里已经能够解决需求了, 然后将 findIdsByValue 函数放入目标循环体中, 循环出目标数组

优化遍历

在数据选择较多的情况下, 上述方法会浪费算力, 重新走重复的路线, 我的思路是能不能深度递归的同时, 把当前的父级路径都记录下来, 然后根据已知的回显数组来判断是否收集当前的路径.

js 复制代码
 function findLabelByValues(root, ids) {
      let stack = [];
      let targetArr = [];
      
      if(Array.isArray(ids)) {
        throw new TypeError('argument type error');
      }
      
      return recursion(root, ids, stack,targetArr);
      
      function recursion(root, ids, stack,targetArr) {
        if (!root) return;
          if (Array.isArray(root)) {
          
            for (let index =  0; index< root.length; index++ ) {
              let iterator = root[index];
              // 同级出栈
              if(index > 0) {
                stack.pop();
              }
              
              stack.push(iterator.id);

              let i = ids.findIndex(item => item == iterator.id);
              
              if(i > -1){
                ids[i] = null;
                // targetArr.push([...stack]);

                // 保证输出顺序
                targetArr[i] = [...stack];
                if(ids.findIndex(item=> item !== null) === -1) {
                  return targetArr;
                }

              }
              
              if (iterator.children && Array.isArray(iterator.children)) {
                  
                  let target =  recursion(
                    iterator.children,
                    ids,
                    stack,
                    targetArr
                  );
                  if(target) {
                    return target;
                  }
                  stack.pop();

              }
            }
         }
      }
    }

这样就完成了一次性的路径收集了, 并且一次深度遍历就能收集所有的路径了, 不用重复的递归

广度优先遍历

完成了深度优先的写法, 我也试着写了广度优先的写法

js 复制代码
function findLabelByValueMore(data, id) {
      let quene = [];
      let resultStack = [];
      
      // 首先将树元素入队列
      quene = data;
      
      // 直到队列元素清空
      // shift() 方法经常用于 while 循环的条件中。下例中每次迭代都会从一个数组中移除下一项元素,直至它成为空数组。
      while(quene.length) {
        // 删除数组第一个值, 并且返回该元素的值, 如果数组为空就返回undefined, 出队列
        let item = quene.shift();
        // 取子节点
        let child = item.children;
         // 判断取值
        if(id === item.id) {
          resultStack.unshift(item.id);
          let up = item.parent;
          // 遍历取值
          while(up) {
            resultStack.unshift(up.id);
            up = up?.parent;
          }
          return resultStack;
        }
        // 遍历子元素进队列
        if(child && Array.isArray(child)) {
              for (let index = 0; index < child.length; index++) {
                const element = child[index];
                quene.push(element);
                // 将父级元素记录在当前元素里面
                element.parent = item;
              }
        } 
      }
   }

关键点是需要记录父节点

总结

上述代码, 未处理边界条件, 和参数限制等, 文中有什么问题希望大神不吝赐教.

相关推荐
じòぴé南冸じょうげん1 小时前
若依框架favicon.ico缓存更新问题解决方案:本地生效但线上未更新
前端·javascript·前端框架·html
狮子座的男孩1 小时前
js基础高级:01、数据类型(typeof、instanceof、===的使用)、数据与变量与内存(定义、赋值与内存关系、引用变量赋值、js调函数传参)
前端·javascript·经验分享·数据类型·数据与变量与内存·赋值与内存关系·引用变量赋值
松涛和鸣2 小时前
14、C 语言进阶:函数指针、typedef、二级指针、const 指针
c语言·开发语言·算法·排序算法·学习方法
yagamiraito_4 小时前
757. 设置交集大小至少为2 (leetcode每日一题)
算法·leetcode·go
星释4 小时前
Rust 练习册 57:阿特巴什密码与字符映射技术
服务器·算法·rust
Cyclo-4 小时前
PDFJS 在React中的引入 使用组件打开文件流PDF
前端·react.js·pdf
无敌最俊朗@4 小时前
力扣hot100-141.环形链表
算法·leetcode·链表
椒盐螺丝钉6 小时前
Vue Router应用:组件跳转
前端·javascript·vue.js
顾安r6 小时前
11.20 开源APP
服务器·前端·javascript·python·css3
WWZZ20257 小时前
快速上手大模型:深度学习10(卷积神经网络2、模型训练实践、批量归一化)
人工智能·深度学习·神经网络·算法·机器人·大模型·具身智能