经典笔试题:扁平化数据结构转树

前言

最近在这三月份,陆陆续续接了了几个笔试题。其中有一个常考的题目。就是后台返回一个扁平的数据结构,转成树。

我们看下题目:打平的数据内容如下:

js 复制代码
let arr = [
    {id: 1, name: '部门1', pid: 0},
    {id: 2, name: '部门2', pid: 1},
    {id: 3, name: '部门3', pid: 1},
    {id: 4, name: '部门4', pid: 3},
    {id: 5, name: '部门5', pid: 4},
]

输出结果

js 复制代码
[
    {
        id: 1,
        name: '部门1',
        children: [
            {
                id: 2,
                name: '部门2',
                children: []
            },
            {
                id: 3,
                name: '部门3',
                children: [
                    {
                        id: 4,
                        name: '部门4',
                        children: [
                            {
                                id: 5,
                                name: '部门5',
                                children: []
                            }
                        ]
                    }
                ]
            }
        ]
    }
]

解决思路

递归

主要思路是提供一个arrayToTree的方法,该方法递归去查找子集。

js 复制代码
/**
 * 递归查找
 */
function arrayToTree(array,pid){  
    let result = []  
    array.forEach(item=>{  
        if(item.pid==pid){  
            item.children = arrayToTree(array,item.id)  
            result.push(item)  
        }  
    })  
    return result  
}

从上面的代码我们分析,该实现的时间复杂度为O(2^n)你。

为什么时间复杂度是O(2^n)

首先,让我们来看函数中的主要操作:

  1. 遍历数组:代码中使用了 forEach 方法对数组进行了一次遍历。这一操作的时间复杂度为 O(n),其中 n 是数组的长度。
  2. 递归调用:在遍历数组的过程中,对于每个节点,都会进行递归调用 arrayToTree 函数来构建其子树。在最坏情况下,每次递归调用都需要遍历一次整个数组来寻找与当前节点的父节点 ID 相同的节点。

因此,递归调用的总时间复杂度为 O(n^2),其中 n 是数组的长度。

不用递归,如何优化

主要思路是先把数据转成Map去存储,之后遍历的同时借助对象的引用,直接从Map找对应的数据做存储。这样借助额外的空间,降低了时间复杂度

js 复制代码
function arrayToTree(items) {
  const result = []; // 存放结果
  const itemMap = {};

  // 先转成map存储
  for (const item of items) {
    itemMap[item.id] = { ...item, children: [] };
  }

  for (const item of items) {
    const id = item.id;
    const pid = item.pid;
    const treeItem = itemMap[id];

    if (pid === 0) {
      result.push(treeItem);
    } else {
      if (!itemMap[pid]) {
        itemMap[pid] = {
          children: [],
        };
      }
      itemMap[pid].children.push(treeItem);
    }
  }
  return result;
}

复杂度为多少

  1. 创建哈希表:在第一个循环中,我们遍历了一次输入的 items 数组,并将每个节点都添加到了哈希表 itemMap 中。这个操作的时间复杂度是 O(n),其中 n 是数组的长度。

  2. 构建树:在第二个循环中,我们再次遍历了一次 items 数组,对每个节点进行处理。对于每个节点,我们都需要访问其父节点,并将当前节点添加到父节点的 children 数组中。在访问和更新节点时,由于使用了哈希表,查找和访问节点的时间复杂度是 O(1),因此对每个节点的操作的时间复杂度也是 O(1)。因此,整个构建树的过程的时间复杂度是 O(n),其中 n 是数组的长度。

综上所述,整个算法的时间复杂度是 O(n),其中 n 是输入数组的长度。这是因为我们只需要遍历两次输入数组,并且在每次操作中都能以常数时间复杂度在哈希表中查找到对应的节点。。

结尾

如果你有着比以上更好的实现方法,欢迎在评论区留下你的答案,大家一起学习。

相关推荐
web130933203982 分钟前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui
呆呆的猫5 分钟前
【LeetCode】227、基本计算器 II
算法·leetcode·职场和发展
Tisfy6 分钟前
LeetCode 1705.吃苹果的最大数目:贪心(优先队列) - 清晰题解
算法·leetcode·优先队列·贪心·
NiNg_1_23423 分钟前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
余额不足1213827 分钟前
C语言基础十六:枚举、c语言中文件的读写操作
linux·c语言·算法
测试老哥31 分钟前
外包干了两年,技术退步明显。。。。
自动化测试·软件测试·python·功能测试·测试工具·面试·职场和发展
如若1231 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python
滚雪球~2 小时前
npm error code ETIMEDOUT
前端·npm·node.js
沙漏无语2 小时前
npm : 无法加载文件 D:\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
supermapsupport2 小时前
iClient3D for Cesium在Vue中快速实现场景卷帘
前端·vue.js·3d·cesium·supermap