TypeScript实现数组和树的相互转换

目录
  • 前言
  • 数组转换为树
  • 树转换为数组
  • 总结

这段时间重新捡起了数据结构和算法,发现里面的树和图是真的掉头发。本文基于一个面试题,详细分析如何实现数组和树的相互转换。

前言

树或者图是个比较抽象的概念,并不存在这样的数据类型。我们看一下树的结构,一层嵌套一层,同层次可能还会有多个节点,这种结构的数据可以使用{}对象来表示。数组就比较简单了,因此数组和树的转换可以理解为数组和对象之间的转换,只是需要转换的数组和对象都是比较特殊的数据。为了更好的看清楚转换过程,本文采用ts的语法,使用js的话没有类型约束,看起来就更容易晕了。

数组转换为树

我们先来一个简单点的,将数组转换为树。

结合树,我们看一下数组的结构:BC是A的子节点,DE是B的子节点,F是C的子节点。想要实现这个效果,我们先来捋一下实现思路:

  • 遍历数组
  • 将数组项转换为树的叶子节点,并使用Map来维持id与节点直接的关系,便于快速查找
  • 根据parentId查找到可能存在的父节点,并建立叶子节点和父节点之间的关系
  • 找到根节点,最终返回的也就是根节点

文字描述可能不太具体,我们看一下实现代码:

// 定义数组项的数据类型,包含id、name、parentId基本属性
interface ArrayItem {
  id: number,
  name: string,
  parentId: number
}
// 定义树节点的数据类型,包含id、name、可能存在的子节点
interface TreeNode {
  id: number,
  name: string,
  children?: TreeNode[], // 叶子节点没有子节点
}
function array2Tree(arr: ArrayItem[]): TreeNode | null {
  // 用于id和TreeNode的映射,map<key, value>,可通过id快速查找到树节点,时间复杂度为O(1)
  const treeNode: Map<number, TreeNode> = new Map();
  let root = null; // 树根节点
  arr.forEach(item => {
    const {id, name, parentId} = item; // 解构赋值
    // 定义树节点tree node,并使用Map维持id与节点之间的关系
    const node: TreeNode = {id, name}
    treeNode.set(id, node);
​
    // 使用parentId找出parentNode
    const parentNode = treeNode.get(parentId);
    if (parentNode) {
      if (parentNode.children == null) {
        parentNode.children = [];
      }
      // 找到父节点后,将当前数组项转换的节点添加到子节点列表中
      parentNode.children.push(node)
    }
​
    // 在第一次循环中找到根节点,我们假定id=0的表示根节点
    if (parentId === 0) {
      root = node;
    }
  })
  return root;
}
const tree = array2Tree(arrs); // 需要定义arrs
console.log(tree);

代码是使用ts完成的,清晰的展示了树节点数据类型的定义,在转换过程中数据类型的变化。直接运行ts代码是看不出来打印的结果的,我们可以新建一个vue-ts的项目,或者嫌麻烦的话可以直接使用官网提供的运行环境:playground

实现效果:

其实就是一个对象,对象的层次就像是树一样展开。

树转换为数组

同样也是这个例子,只不过是需要将树转换为数组了。

数组和树在数据结构上最大的差异就是树没有parentId的属性,如果一个节点有子节点,那么该节点的id就应该是其子节点的parentId。老规矩,先来捋一下思路:

  • 遍历树节点,使用广度优先。树的遍历可分为广度优先和深度优先,广度优先表示横向遍历,深度优先表示纵向遍历
  • 将遍历出的树节点添加到队列中,实现先进先出。队列并不是js中的数据类型,但是我们可以使用数组来实现队列
  • 将树节点转换为数组项
  • 根据节点的父子关系,设置数组项的parentId,并将数组项添加到数组中
  • 为了查找遍历,可使用Map来维持父节点和子节点的关系

实现代码:

// 定义数据结构
...
function tree2Array(root: TreeNode): ArrayItem[] {
  // 定义子节点和父节点的映射关系,map<子节点,父节点>
  const childToParent: Map<TreeNode, TreeNode> = new Map();

  const arr: ArrayItem[] = []; // 返回的数组

  // 广度优先遍历树,使用队列保存节点
  const queue: TreeNode[] = [];
  queue.unshift(root); // 入队,从头部入队
  while(queue.length) {
    const curNode = queue.pop(); // 出队,从尾部出队,保证先进先出
    if (curNode == null) break; // 循环结束,使用==包括了null和undefined的情况

    const {id, name, children = []} = curNode; // 结构赋值,叶子节点没有children属性,所以默认为空数组

    const parentNode = childToParent.get(curNode); // 获取当前节点的父节点
    const parentId = parentNode?.id || 0; // 获取父节点id,根节点设置为0
    const item = {id, name, parentId}; // 定义数组项
    arr.push(item); // 插入到数组中

    // 继续遍历子节点,并且子节点入队
    children.forEach(child => {
      // 如果有子节点,则把子节点和当前节点通过Map保持关系
      childToParent.set(child, curNode);

      // 入队
      queue.unshift(child);
    })
  }

  return arr;
}
const array = tree2Array(obj); // 这个obj对象就是数组转换为树打印出的结果
console.log(array)

运行结果:

可以发现,结果符合预期。

总结

总结一下,数组和树之间相互转换一开始还挺蒙的,但是看完代码发现,其实也并不复杂。

  • 数组转换为树:从数组的第一项开始,根据parentId,找到每一项的归属;若找不到,则表示当前数组项为根节点
  • 树转换为数组:树的每一个节点都可能会有很多子节点,按照广度优先,从根节点开始遍历,将每一个节点以及其子节点都添加到队列中,按照先进先出的原则,逐一将节点都转换为数组项并添加到数组中
  • 在实现的过程中,可结合图例,一步一步分析,最终实现结果

以上就是TypeScript实现数组和树的相互转换的详细内容,更多关于TypeScript数组转树的资料请关注我们其它相关文章!

(0)

相关推荐

  • 如何将JavaScript将数组转为树形结构

    1.需求 后台给了一个这样的数据让咱前端去转换为树形结构(没有重复数据).不多说,先来看看给了一个怎样的数组数据,转换为怎样的树形结构. 服务器传过来的数组 const arr = [ [ {"deptId":"D019", "deptName":"销售部"}, {"deptId":"D019101", "deptName":"华北销售中心"} ]

  • TypeScript 数组Array操作的常用方法

    目录 一.数组的声明 二.数组初始化 三.数组元素赋值.添加.更改 四.删除 五.合并.断开数组 六.查找数组元素位置 七.连接数组元素 八.排序.反序数组 九.遍历请看这里 数组是一个很简单的数据结构,但是每次使用TypeScript的数组的时候又总是忘记怎么用了,干脆直接弄成干货,忘了过来看看. 一.数组的声明 let array1:Array<number>; let array2:number[]; 二.数组初始化 let array1:Array<number> = ne

  • js 实现 list转换成tree的方法示例(数组到树)

    目标: JS 将有父子关系的平行数组转换成树形数据 方法:双重遍历,一次遍历parentId,一次遍历id == parendId; 该方法应该能很容易被想到,实现起来也一步一步可以摸索出来: const oldData = [ {id:1,name:'boss',parentId:0}, {id:2,name:'lily',parentId:1}, {id:3,name:'jack',parentId:1}, {id:4,name:'john',parentId:2}, {id:5,name:

  • TypeScript调整数组元素顺序算法

    目录 前言 实现思路 实现代码 代码的可扩展性 测试用例 示例代码 总结 前言 有一个整数数组,我们想按照特定规则对数组中的元素进行排序,比如:数组中的所有奇数位于数组的前半部分. 本文将带大家实现这个算法,欢迎各位感兴趣的开发者阅读本文. 实现思路 我们通过一个实例来分析下:假设有这样一个数组:[2, 4, 5, 6, 7, 8, 9, 11],将奇数移动到最前面后,就是:[11, 9, 5, 7, 6, 8, 4, 2]. 通过观察后,我们发现在扫描这个数组的时候,如果发现有偶数出现在奇数的

  • JavaScript平铺数组转树形结构的实现示例

    目录 后台丢来了1w条数据 递归方式 非递归方式 总结 后台丢来了1w条数据 千算万算,还是没有逃过,后台真的就上万条数据一次丢给前端了.好吧,好在还不是10w条.如下,后台返回的是这样的结构: const flatArr = [ { id: '001', name: '节点1' }, { id: '0011', parentId: '001', name: '节点1-1' }, { id: '00111', parentId: '0011', name: '节点1-1-1' }, { id:

  • TypeScript实现数组和树的相互转换

    目录 前言 数组转换为树 树转换为数组 总结 这段时间重新捡起了数据结构和算法,发现里面的树和图是真的掉头发.本文基于一个面试题,详细分析如何实现数组和树的相互转换. 前言 树或者图是个比较抽象的概念,并不存在这样的数据类型.我们看一下树的结构,一层嵌套一层,同层次可能还会有多个节点,这种结构的数据可以使用{}对象来表示.数组就比较简单了,因此数组和树的转换可以理解为数组和对象之间的转换,只是需要转换的数组和对象都是比较特殊的数据.为了更好的看清楚转换过程,本文采用ts的语法,使用js的话没有类

  • C#byte数组与Image的相互转换实例代码

    C#byte数组与Image的相互转换实例代码 功能需求: 1.把一张图片(png bmp jpeg bmp gif)转换为byte数组存放到数据库. 2.把从数据库读取的byte数组转换为Image对象,赋值给相应的控件显示. 3.从图片byte数组得到对应图片的格式,生成一张图片保存到磁盘上. 这里的Image是System.Drawing.Image. //Get an image from file Image image = Image.FromFile("D:\\test.jpg&q

  • PHP实现数组和对象的相互转换操作示例

    本文实例讲述了PHP实现数组和对象的相互转换操作.分享给大家供大家参考,具体如下: 关于php中想让对象以数组的形式访问,这时候就需要使用到get_object_vars()函数了.先来介绍一下这个函数. 官方文档是这样解释的: array get_object_vars ( object $obj ) 返回由 obj 指定的对象中定义的属性组成的关联数组. 举一个栗子: <?php class Point2D { var $x, $y; var $label; function Point2D

  • Java中数组与集合的相互转换实现解析

    这篇文章主要介绍了Java中数组与集合的相互转换实现解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 List转数组:采用集合的toArray()方法 数组转List:采用Arrays的asList()方法 数组转换为集合 注意:在数组转集合的过程中,要注意是否使用了视图的方式直接返回数组中的数据.以Arrays.asList()为例,它把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear方法会抛出 Unsu

  • Numpy中矩阵matrix读取一列的方法及数组和矩阵的相互转换实例

    Numpy matrix 必须是2维的,但是 numpy arrays (ndarrays) 可以是多维的(1D,2D,3D····ND),matrix是Array的一个小的分支,包含于Array. import numpy as np >>> m = np.mat([[1,2],[3,4]]) >>> m[0] #读取一行 matrix([[1, 2]]) >>> m[:,0] #读取一列 matrix([[1], [3]]) numpy中数组和矩阵

  • js实现数组转树示例

    目录 原生 封装工具函数 getTree 结构图: 原生 封装工具函数 getTree 1.1 定义 -映射对象 map 数组 treeList=[] 1.2 遍历后端返回的数组 list 为 每个数组对象item 添加 children 属性 值为空数组 1.3 为映射对象 map 添加属性 并赋值key:id值 value:item 1.4 遍历数组对象list 当item.pid为空时 为一级目录 将该一级目录数组对象 添加到treeList中 1.5 通过 item.pid获取到 id

  • Java中实现双数组Trie树实例

    传统的Trie实现简单,但是占用的空间实在是难以接受,特别是当字符集不仅限于英文26个字符的时候,爆炸起来的空间根本无法接受. 双数组Trie就是优化了空间的Trie树,原理本文就不讲了,请参考An Efficient Implementation of Trie Structures,本程序的编写也是参考这篇论文的. 关于几点论文没有提及的细节和与论文不一一致的实现: 1.对于插入字符串,如果有一个字符串是另一个字符串的子串的话,我是将结束符也作为一条边,产生一个新的结点,这个结点新节点的Ba

  • js数组与字符串的相互转换方法

    熟悉js的朋友很多都遇到过js的数组与字符串相互转换的情况,本文就此作一简单介绍,示例如下: 一.数组转字符串 需要将数组元素用某个字符连接成字符串,示例代码如下: var a, b; a = new Array(0,1,2,3,4); b = a.join("-"); 二.字符串转数组 实现方法为将字符串按某个字符切割成若干个字符串,并以数组形式返回,示例代码如下: var s = "abc,abcd,aaa"; ss = s.split(","

随机推荐