# 常用树形结构算法分享

·  阅读 443

## 2.递归处理

``````function handleTreeData(data) {
let arr = [];
data.forEach((item) => {
arr.push({
...item,
title: item.name,
key: item.id,
children:
item.children && item.children.length
? handleTreeData(item.children)
: [],
});
});
return arr;
}

let treedata = [
{
name: "1-1",
id: 1,
parentId: 0,
children: [
{
name: "1-1-1",
id: 2,
parentId: 1,
},
],
}
];

## 3.遍历树：

BFS是从根节点开始，沿着树的宽度遍历树的节点。 如果所有节点均被访问，则算法中止。

``````let tree = [
{
id: "1",
title: "节点1",
children: [
{
id: "1-1",
title: "节点1-1",
},
{
id: "1-2",
title: "节点1-2",
},
],
},
{
id: "2",
title: "节点2",
children: [
{
id: "2-1",
title: "节点2-1",
},
],
},
];

// 广度优先
function treeForeach(tree, func) {
let node;
let list = [...tree];
while ((node = list.shift())) {
func(node);
node.children && list.push(...node.children);
//每次将节点的children元素push到list后面，不断循环，知道最后一层
}
}
treeForeach(tree, (node) => {
console.log(node.title);
});

## 4.树形数据结构化

### 1.列表转换为树：

``````let list = [
{
id: "1",
title: "节点1",
parentId: "",
},
{
id: "1-1",
title: "节点1-1",
parentId: "1",
},
{
id: "1-2",
title: "节点1-2",
parentId: "1",
},
{
id: "2",
title: "节点2",
parentId: "",
},
{
id: "2-1",
title: "节点2-1",
parentId: "2",
},
{
id: "3-1",
title: "节点3-1",
parentId: "2-1",
},
];

//网上简易版
function listToTree(list) {
let info = list.reduce(
(map, node) => ((map[node.id] = node), (node.children = []), map),
{}
);
return list.filter((node) => {
info[node.parentId] && info[node.parentId].children.push(node);
return !node.parentId;
});
}
// console.log(listToTree(list));

//新手理解版
function formatDataTree(list) {
let parents = list.filter((p) => p.parentId === "");
let childrens = list.filter((c) => c.parentId !== "");

dataToTree(parents, childrens);

return parents;
function dataToTree(parents, childrens) {
parents.map((p) => {
childrens.map((c, i) => {
if (c.parentId === p.id) {
let _children = JSON.parse(JSON.stringify(childrens));
_children.splice(i, 1);
dataToTree([c], _children);
if (p.children) {
//判断父元素是否存在children
p.children.push(c);
} else {
p.children = [c];
}
}
});
});
}
}

console.log(formatDataTree(list));

### 2.树转换为列表：

``````let tree = [
{
id: "1",
title: "节点1",
children: [
{
id: "1-1",
title: "节点1-1",
},
{
id: "1-2",
title: "节点1-2",
},
],
},
{
id: "2",
title: "节点2",
children: [
{
id: "2-1",
title: "节点2-1",
},
],
},
];
//方法一：递归实现
function treeToList(tree, result = [], level = 0) {
tree.forEach((node) => {
result.push(node);
node.level = level + 1;
node.children && treeToList(node.children, result, level + 1);
});
return result;
}
console.log(treeToList(tree))

// 方法二：循环实现
//   function treeToList (tree) {
//     let node, result = tree.map(node => (node.level = 1, node))
//     for (let i = 0; i < result.length; i++) {
//       if (!result[i].children) continue
//       let list = result[i].children.map(node => (node.level = result[i].level + 1, node))
//       result.splice(i+1, 0, ...list)
//     }
//     return result
//   }

//方法三：网上简易版
function treeToList(tree) {
return (tree || []).reduce((total, current) => {
const { children, ...obj } = current;
}, []);
}

## 5.树结构查找

### 查找节点：

``````let tree = [
{
id: "1",
title: "节点1",
children: [
{
id: "1-1",
title: "节点1-1",
},
{
id: "1-2",
title: "节点1-2",
},
],
},
{
id: "2",
title: "节点2",
children: [
{
id: "2-1",
title: "节点2-1",
},
],
},
];

//深度优先
function treeFind(tree, func) {
for (const data of tree) {
if (func(data)) return data; //有就直接返回
if (data.children) { //没有就进去子节点查找，递归调用
const res = treeFind(data.children, func);
if (res) return res;
}
}
return null; //都没找到，返回null
}
function findsingle(data) {
if (data.title === "节点1-2") {
return true;
}
}

console.log(treeFind(tree,findsingle)) //{ id: '1-2', title: '节点1-2' }

``````//广度优先
function treeFind(tree, func) {
for (const data of tree) {
if (func(data)) return data //直接循环第一层的所有节点
}

const chidrens = tree.reduce((total, current) => {
}, []);

const res =  treeFind(chidrens, func);
if (res) return res;
return null;
}

### 查找节点路径：

``````let tree = [
{
id: "1",
title: "节点1",
children: [
{
id: "1-1",
title: "节点1-1",
},
{
id: "1-2",
title: "节点1-2",
},
],
},
{
id: "2",
title: "节点2",
children: [
{
id: "2-1",
title: "节点2-1",
},
],
},
];
function treeFindPath(tree, func, path = []) {
if (!tree) return []; //空判断
for (const data of tree) {
path.push(data.id);
if (func(data)) return path;
if (data.children) {
const findChildren = treeFindPath(data.children, func, path);
if (findChildren.length) return findChildren;
}
path.pop();
}
return [];
}

function findsingle(data) {
if (data.title === "节点1-2") {
return true;
}
}

console.log(treeFindPath(tree, findsingle, (path = []))) //[ '1', '1-2' ]

### 查找多条节点路径

``````let tree = [
{
id: "1",
title: "节点1",
children: [
{
id: "1-1",
title: "节点1-1",
},
{
id: "1-2",
title: "节点1-2",
},
],
},
{
id: "2",
title: "节点2",
children: [
{
id: "2-1",
title: "节点2-1",
},
],
},
];
function treeFindPath(tree, func, path = [], result = []) {
for (const data of tree) {
path.push(data.id);
func(data) && result.push([...path]);
data.children && treeFindPath(data.children, func, path, result);
path.pop();
}
return result;
}

function findsingle(data) {
if (data.title === "节点1-2") {
return true;
}
}
console.log(treeFindPath(tree, findsingle)) //[ [ '1', '1-2' ] ]

## 6.其他常用算法

### 树的最大深度

``````let treedata = {
name: "1-1",
id: 1,
parentId: 0,
children: [
{
name: "1-1-1",
id: 2,
parentId: 1,
children: [],
},
{
name: "1-1-3",
id: 4,
parentId: 1,
children: [
{
name: "1-1-3-1",
id: 5,
parentId: 4,
children: [],
},
{
name: "1-1-3-2",
id: 6,
parentId: 4,
children: [],
},
],
},
],
};
maxDepth = (root) => {
if (root === null) {
return 0;
}
var max = 0;
for (var i = 0; i < root.children.length; i++) {
max = Math.max(max, maxDepth(root.children[i]));
}
return max + 1;
};

### 树的总节点个数

``````let treedata = [
{
name: "1-1",
id: 1,
parentId: 0,
children: [
{
name: "1-1-1",
id: 2,
parentId: 1,
children: [],
},
{
name: "1-1-3",
id: 4,
parentId: 1,
children: [
{
name: "1-1-3-1",
id: 5,
parentId: 4,
children: [],
},
{
name: "1-1-3-2",
id: 6,
parentId: 4,
children: [],
},
],
},
],
},
];

let leafcount = 0;
function treeNodecount(data) {
let arr = [];
data.forEach((item) => {
leafcount++;
arr.push({
children:
item.children && item.children.length
? treeNodecount(item.children)
: [],
});
});
return arr;
}

treeNodecount(treedata)
console.log(leafcount)

### 树结构筛选

``````let tree = [
{
id: "1",
title: "节点1",
children: [
{
id: "1-1",
title: "节点1-1",
},
{
id: "1-2",
title: "节点1-2",
},
],
},
{
id: "2",
title: "节点2",
children: [
{
id: "2-1",
title: "节点2-1",
},
],
},
];

function treeFilter(tree, func) {
// 使用map复制一下节点，避免修改到原树
return tree
.map((node) => ({ ...node }))
.filter((node) => {
node.children = node.children && treeFilter(node.children, func);
return func(node) || (node.children && node.children.length);
});
}

function findsingle(data) {
if (data.title === "节点1") {
return true;
}
}
console.log(treeFilter(tree,findsingle))
// {
//     id: "1"
//     title: "节点1",
//     children:[]
// }

### 根据条件从树形结构中解析数据：

``````let tree = [
{
name: "1-1",
id: 1,
parentId: 0,
type: "user",
children: [
{
name: "1-1-1",
id: 2,
parentId: 1,
children: [],
type: "manager",
},
{
name: "1-1-3",
id: 4,
parentId: 2,
type: "user",
children: [
{
name: "1-1-3-1",
id: 5,
parentId: 4,
children: [],
type: "manager",
},
{
name: "1-1-3-2",
id: 5,
parentId: 4,
children: [],
type: "manager",
}
],
},
],
},
];

function parseTreeData(tree) {
return (tree || []).reduce((total, current) => {
const { type, children, ...obj } = current;
if (type === "manager") {
}
const paserChildren = parseTreeData(children);

}, []);
}

console.log(parseTreeData(tree));