扁平数据转tree结构

713 阅读2分钟

测试数据:

const data = [
	{ id: 1, pid: 0 },
	{ id: 2, pid: 1 },
	{ id: 3, pid: 1 },
	{ id: 4, pid: 3 },
	{ id: 5, pid: 4 },
    ]

// 转换为
{
  id: 1, 
  pid: 0,
  children:[
    {
      id: 2,
      pid: 1,
    },
    {
      id: 3,
      pid: 1,
      children:[
        {
          id: 4,
           pid: 3,
          children:[
            {
              id: 5,
              pid: 4,
            }
          ]
        }
      ]
    }
  ]
}

思路:首先找到父级元素,然后遍历数组往父元素的children中push数据,通过递归向下一层数据中继续添加。

// 1.先遍历获取最外层的父元素
const parentArr = data.forEach(item => !item.pid);

// 2.递归生成树结构
const toTree = (parent, child) => {
  parent.forEach(item => {
    // 添加children属性
    item.children = [];
    let list = [];
    child.forEach(i => { 
      if(i.pid && i.pid===item.id) {
        item.children.push(i);
        list.push(i);
      }
    })
    toTree(list, child);
  })
  return parent
}

toTree(parentArr, data);

那如何把tree结构转换为普通的扁平数据呢?

let list =[];
const transformTree = (arr) => {
  arr.forEach(item => {
    if(item.children) {
       if(item.children.length) {
	 let node = [...item.children];
         transformTree(node);
      }
      delete item.children;
    }
    list.push(item);
  })
}

然而,递归算法虽然简洁,但是效率却很低O(2^n),通常不作为推荐算法。

补充:

如何衡量算法的效率呢:一般从算法时间复杂度空间复杂度来衡量。

  • 时间复杂度:算法执行语句的次数。

    计算方法:

    1. 找出算法中的基本语句:算法中执行次数最多的那条语句就是基本语句,通常是最内层循环的循环体

    2. 计算基本语句的执行次数的数量级:只需保留f(n)中的最高次幂正确即可,可以忽略所有低次幂和最高次幂的系数

      如果算法中包含嵌套的循环,则基本语句通常是最内层的循环体,如果算法中包含并列的循环,则将并列循环的时间复杂度相加, 例如:

      for(i=1; i<n; i++) x++;

      for(i=1, i<n; i++){

      ​ for(j=1; j<n; j++) x++;

      }

      第一个for循环的时间复杂度为Ο(n),第二个for循环的时间复杂度为Ο(n²),则整个算法的时间复杂度为Ο(n+n²)=Ο(n²)。

      注、加法原则:T(n)=O(f(n))+O(g(n))=O(max(fn,gn))

      常见的算法时间复杂度由小到大依次为:Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n²)<Ο(n³)<…<Ο(2^n)<Ο(n!)<O(n^n)

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

    计算方法:

    1. 忽略常数,用O(1)表示

    2. 递归算法的空间复杂度=(递归深度n)*(每次递归所要的辅助空间)

非递归来, 使用Map结构实现。

	const toTree = (list = [], { rootid = 0 } = {}) => {
		const parentMap = new Map();
		const topNodes = [];
		list.forEach(item => {
			if (parentMap.has(item.id)) {
			    item.children = parentMap.get(item.id).children;
			} else {
			    item.children = [];
			}

			parentMap.set(item.id, item);

			if (parentMap.has(item.pid)) {
			    parentMap.get(item.pid).children.push(item);
			} else {
			    parentMap.set(item.pid, { children: [item] });
			}
			if (item.pid === rootid) {
			    topNodes.push(item);
			}
		})
		return topNodes;
	}
	
	toTree(data, { rootid: 0 })