中级算法题:实现数组和树的相互转换(ts版)

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

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

前言

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

数组转换为树

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

树.png

结合树,我们看一下数组的结构: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

实现效果:

image-20220622144731183.png

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

树转换为数组

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

树.png

数组和树在数据结构上最大的差异就是树没有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)

运行结果:

image-20220622150913875.png

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

总结

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

  • 数组转换为树:从数组的第一项开始,根据parentId,找到每一项的归属;若找不到,则表示当前数组项为根节点
  • 树转换为数组:树的每一个节点都可能会有很多子节点,按照广度优先,从根节点开始遍历,将每一个节点以及其子节点都添加到队列中,按照先进先出的原则,逐一将节点都转换为数组项并添加到数组中
  • 在实现的过程中,可结合图例,一步一步分析,最终实现结果
相关文章
|
8月前
|
存储 机器学习/深度学习 监控
网络管理监控软件的 C# 区间树性能阈值查询算法
针对网络管理监控软件的高效区间查询需求,本文提出基于区间树的优化方案。传统线性遍历效率低,10万条数据查询超800ms,难以满足实时性要求。区间树以平衡二叉搜索树结构,结合节点最大值剪枝策略,将查询复杂度从O(N)降至O(logN+K),显著提升性能。通过C#实现,支持按指标类型分组建树、增量插入与多维度联合查询,在10万记录下查询耗时仅约2.8ms,内存占用降低35%。测试表明,该方案有效解决高负载场景下的响应延迟问题,助力管理员快速定位异常设备,提升运维效率与系统稳定性。
351 4
|
存储 机器学习/深度学习 算法
KMP、Trie树 、AC自动机‌ ,三大算法实现 优雅 过滤 netty 敏感词
KMP、Trie树 、AC自动机‌ ,三大算法实现 优雅 过滤 netty 敏感词
KMP、Trie树 、AC自动机‌ ,三大算法实现 优雅 过滤 netty  敏感词
|
11月前
|
监控 算法 安全
基于 C# 基数树算法的网络屏幕监控敏感词检测技术研究
随着数字化办公和网络交互迅猛发展,网络屏幕监控成为信息安全的关键。基数树(Trie Tree)凭借高效的字符串处理能力,在敏感词检测中表现出色。结合C#语言,可构建高时效、高准确率的敏感词识别模块,提升网络安全防护能力。
281 2
|
监控 算法 数据处理
基于 C++ 的 KD 树算法在监控局域网屏幕中的理论剖析与工程实践研究
本文探讨了KD树在局域网屏幕监控中的应用,通过C++实现其构建与查询功能,显著提升多维数据处理效率。KD树作为一种二叉空间划分结构,适用于屏幕图像特征匹配、异常画面检测及数据压缩传输优化等场景。相比传统方法,基于KD树的方案检索效率提升2-3个数量级,但高维数据退化和动态更新等问题仍需进一步研究。未来可通过融合其他数据结构、引入深度学习及开发增量式更新算法等方式优化性能。
310 17
|
存储 监控 算法
局域网上网记录监控的 C# 基数树算法高效检索方案研究
在企业网络管理与信息安全领域,局域网上网记录监控是维护网络安全、规范网络行为的关键举措。随着企业网络数据量呈指数级增长,如何高效存储和检索上网记录数据成为亟待解决的核心问题。基数树(Trie 树)作为一种独特的数据结构,凭借其在字符串处理方面的卓越性能,为局域网上网记录监控提供了创新的解决方案。本文将深入剖析基数树算法的原理,并通过 C# 语言实现的代码示例,阐述其在局域网上网记录监控场景中的具体应用。
277 7
|
人工智能 算法 语音技术
Video-T1:视频生成实时手术刀!清华腾讯「帧树算法」终结闪烁抖动
清华大学与腾讯联合推出的Video-T1技术,通过测试时扩展(TTS)和Tree-of-Frames方法,显著提升视频生成的连贯性与文本匹配度,为影视制作、游戏开发等领域带来突破性解决方案。
508 4
Video-T1:视频生成实时手术刀!清华腾讯「帧树算法」终结闪烁抖动
|
算法 Java
算法系列之数据结构-Huffman树
Huffman树(哈夫曼树)又称最优二叉树,是一种带权路径长度最短的二叉树,常用于信息传输、数据压缩等方面。它的构造基于字符出现的频率,通过将频率较低的字符组合在一起,最终形成一棵树。在Huffman树中,每个叶节点代表一个字符,而每个字符的编码则是从根节点到叶节点的路径所对应的二进制序列。
454 3
 算法系列之数据结构-Huffman树
|
机器学习/深度学习 算法 搜索推荐
决策树算法如何读懂你的购物心理?一文看懂背后的科学
"你为什么总能收到刚好符合需求的商品推荐?你有没有好奇过,为什么刚浏览过的商品就出现了折扣通知?
352 0
|
存储 人工智能 算法
C 408—《数据结构》算法题基础篇—数组(通俗易懂)
408考研——《数据结构》算法题基础篇之数组。(408算法题的入门)
960 23
|
存储 监控 算法
关于员工上网监控系统中 PHP 关联数组算法的学术解析
在当代企业管理中,员工上网监控系统是维护信息安全和提升工作效率的关键工具。PHP 中的关联数组凭借其灵活的键值对存储方式,在记录员工网络活动、管理访问规则及分析上网行为等方面发挥重要作用。通过关联数组,系统能高效记录每位员工的上网历史,设定网站访问权限,并统计不同类型的网站访问频率,帮助企业洞察员工上网模式,发现潜在问题并采取相应管理措施,从而保障信息安全和提高工作效率。
251 7