浅谈JavaScript构造树形结构的一种高效算法

 更新时间:2021年05月08日 10:08:18   作者:李中凯  
这篇文章主要介绍了JavaScript构造树形结构的一种高效算法,对算法感兴趣的同学,可以参考下

引言

我们经常会碰到树形数据结构,比如组织层级、省市县或者动植物分类等等数据。下面是一个树形结构的例子:

在实际应用中,比较常见的做法是将这些信息存储为下面的结构,特别是当存在1对多的父/子节点关系时:

const data = [
  { id: 56, parentId: 62 },
  { id: 81, parentId: 80 },
  { id: 74, parentId: null },
  { id: 76, parentId: 80 },
  { id: 63, parentId: 62 },
  { id: 80, parentId: 86 },
  { id: 87, parentId: 86 },
  { id: 62, parentId: 74 },
  { id: 86, parentId: 74 },
];

那么,如何将这种对象数组格式转换为层级树的格式呢?其实,利用 JavaScript 对象引用的特性,实现起来会非常简单。它可以不用递归,在O(n)时间内完成。

术语

为了表述方便,我们先来定义几个术语。我们把数组中的每个元素(也就树形图里的每个圆圈)称为“节点”。节点可以是多个节点的“父节点”,也可以是某个节点的“子节点”。上图中,节点 86是节点 80和节点 87的“父节点”,节点 86是节点 74的子节点。树的最顶部节点称为“根节点”。

思路

为了构造树形结构,我们需要:

  • 遍历data数组
  • 找到当前元素的父元素
  • 在父元素对象上添加一个对该子元素的引用
  • 元素如果没有父元素,那我们就认为它是树的根节点

我们可以看到到,引用被保存在对象树下,这就是为什么我们可以在O(n)时间内完成这个任务!

建立 ID-数组索引映射关系

虽然不是必需的,但是这个映射关系可以帮我们快速找到元素的位置,方便找到到父元素的引用。

const idMapping = data.reduce((acc, el, i) => {
  acc[el.id] = i;
  return acc;
}, {});

映射结果如下,后面你会看到它的用处有多大:

{

  56: 0,

  62: 7,

  63: 4,

  74: 2,

  76: 3,

  80: 5,

  81: 1,

  86: 8,

  87: 6,

};

构造树形结构

现在我们开始构造这个树形结构。遍历这个对象数组,找到每个元素的父元素对象,然后添加对这个元素的引用。现在你应该看到了,这个idMapping用来定位元素的位置多么方便(常数时间)。

let root;
data.forEach(el => {
  // 判断根节点
  if (el.parentId === null) {
    root = el;
    return;
  }
  // 用映射表找到父元素
  const parentEl = data[idMapping[el.parentId]];
  // 把当前元素添加到父元素的`children`数组中
  parentEl.children = [...(parentEl.children || []), el];
});

完事!用console.log打印root看下:

console.log(root);

{

  id: 74,

  parentId: null,

  children: [

    {

      id: 62,

      parentId: 74,

      children: [{ id: 56, parentId: 62 }, { id: 63, parentId: 62 }],

    },

    {

      id: 86,

      parentId: 74,

      children: [

        {

          id: 80,

          parentId: 86,

          children: [{ id: 81, parentId: 80 }, { id: 76, parentId: 80 }],

        },

        { id: 87, parentId: 86 },

      ],

    },

  ],

};

原理

为什么可以这么做呢?这是因为,data数组里的每个元素都是内存里的一个对象引用,forEach循环里的el变量其实是指向内存里的一个对象,parentEl也引用了一个对象。

如果内存中的一个对象引用了一个 children 数组,这些子元素同样可以引用自己的子元素数组,这些关联关系都是通过引用完成的。

总结

对象引用是 JavaScript 中最基本的概念之一,需要更多的学习和理解。真正理解这个概念后,既可以避免棘手的 bug,又可以为看似复杂的问题提供相对简单的解决方案。

以上就是浅谈JavaScript构造树形结构的一种高效算法的详细内容,更多关于JavaScript构造树形结构的算法的资料请关注脚本之家其它相关文章!

相关文章

  • layui问题之渲染数据表格时,仅出现10条数据的解决方法

    layui问题之渲染数据表格时,仅出现10条数据的解决方法

    今天小编就为大家分享一篇layui问题之渲染数据表格时,仅出现10条数据的解决方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-09-09
  • JavaScript处理变量命名的参数对象

    JavaScript处理变量命名的参数对象

    这篇文章主要介绍了JavaScript处理变量命名的参数对象,在开发过程中,遇到一个给对象赋值的问题,参数是通过循环变量的方式进行处理,接下来详细介绍需要的小伙伴可以参考一下
    2022-06-06
  • 纯js的右下角弹窗实例

    纯js的右下角弹窗实例

    下面小编就为大家带来一篇纯js的右下角弹窗实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • jQuery检测输入的字符串包含的中英文的数量

    jQuery检测输入的字符串包含的中英文的数量

    这篇文章主要介绍了jQuery检测输入的字符串包含的中英文的数量的实现方法,非常的实用,这里推荐给小伙伴,有需要的朋友可以参考下。
    2015-04-04
  • 详解JavaScript运算符中==和===的区别

    详解JavaScript运算符中==和===的区别

    在JavaScript中==运算符和===运算符是经常遇到的,那么二者有哪些区别呢,本文就来和大家进行简单的讨论,感兴趣的小伙伴可以跟随小编一起学习学习
    2023-05-05
  • JavaScript 闭包在封装函数时的简单分析

    JavaScript 闭包在封装函数时的简单分析

    近才开始系统的研究js,对js的兴趣源于对JQuery的应用。之前只会用js做简单的计算函数,后来由于需要做特效,故接触JQ,看着API,基本的特效都能完成,但相反,如果用js去实现,估计自己很难写得出来,所以下定决心系统的看看js。
    2009-11-11
  • JavaScript正则表达式小结(test|match|search|replace|split|exec)

    JavaScript正则表达式小结(test|match|search|replace|split|exec)

    这篇文章主要介绍了JavaScript正则表达式小结(test|match|search|replace|split|exec)的相关资料,非常不错,具有参考借鉴价值,需要的朋友参考下吧
    2016-12-12
  • IntelliJ IDEA 安装vue开发插件的方法

    IntelliJ IDEA 安装vue开发插件的方法

    本篇文章主要介绍了IntelliJ IDEA 安装vue开发插件的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11
  • javascript实现分栏显示小技巧附图

    javascript实现分栏显示小技巧附图

    考试页面可以实现隐藏左边的考生信息部分,学了javascript后也能实现这个功能了,下面是实现思路、代码及解效果截图,喜欢的朋友们可以看看
    2014-10-10
  • AutoSave/自动存储功能实现

    AutoSave/自动存储功能实现

    AutoSave/自动存储功能实现...
    2007-03-03

最新评论