林秀栋的技术博客

扁平数据结构转Tree

文章来源

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 },
];

// 转为
arr2 = {
  id: 1,
  name: "部门1",
  pid: 0,
  children: [
    {
      id: 2,
      name: "部门2",
      pid: 1,
      children: [],
    },
    {
      id: 3,
      name: "部门3",
      pid: 1,
      children: [
        // 结果 ,,,
      ],
    },
  ],
};

时间复杂度

时间复杂度的计算并不是计算程序具体运行的时间,而是算法执行语句的次数。

随着 n 的不断增大,时间复杂度不断增大,算法花费时间越多。 常见的时间复杂度有

计算方法

  1. 选取相对增长最高的项
  2. 最高项系数是都化为 1
  3. 若是常数的话用 O(1)表示

举个例子:如 f(n)=3*n^4+3n+300 则 O(n)=n^4

通常我们计算时间复杂度都是计算最坏情况。计算时间复杂度的要注意的几个点

let x = 1;
while (x < 100) {
  x++;
}
for (i = 0; i < n; i++) {
  for (j = 0; j < n; j++) {
    // ...code
  }
}
for (var i = 0; i < n && arr[i] != 1; i++) {
  // ...code
}

空间复杂度

空间复杂度是对一个算法在运行过程中临时占用存储空间的大小。

计算方法:

计算空间复杂度的简单几点

let a = 1;
let b = 2;
let c = 3;
console.log("输出a,b,c", a, b, c);
function fun(n) {
  let k = 10;
  if (n == k) {
    return n;
  } else {
    return fun(++n);
  }
}

不考虑性能实现,递归遍历查找

主要思路是提供一个递 getChildren 的方法,该方法递归去查找子集。 就这样,不用考虑性能,无脑去查,大多数人只知道递归,就是写不出来。。。

/**
 * 递归查找,获取children
 */
const getChildren = (data, result, pid) => {
  for (const item of data) {
    if (item.pid === pid) {
      const newItem = { ...item, children: [] };
      result.push(newItem);
      getChildren(data, newItem.children, item.id);
    }
  }
};

/**
 * 转换方法
 */
const arrayToTree = (data, pid) => {
  const result = [];
  getChildren(data, result, pid);
  return result;
};

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

不用递归,也能搞定

主要思路是先把数据转成 Map 去存储,之后遍历的同时借助对象的引用,直接从 Map 找对应的数据做存储

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;
}

从上面的代码我们分析,有两次循环,该实现的时间复杂度为 O(2n),需要一个 Map 把数据存储起来,空间复杂度 O(n)

最优性能

主要思路也是先把数据转成 Map 去存储,之后遍历的同时借助对象的引用,直接从 Map 找对应的数据做存储。不同点在遍历的时候即做 Map 存储,有找对应关系。性能会更好。

function arrayToTree(items) {
  const result = []; // 存放结果集
  const itemMap = {}; //
  for (const item of items) {
    const id = item.id;
    const pid = item.pid;

    if (!itemMap[id]) {
      itemMap[id] = {
        children: [],
      };
    }

    itemMap[id] = {
      ...item,
      children: itemMap[id]["children"],
    };

    const treeItem = itemMap[id];

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

从上面的代码我们分析,一次循环就搞定了,该实现的时间复杂度为 O(n),需要一个 Map 把数据存储起来,空间复杂度 O(n)