什么是树
树是一种分层数据的抽象模型。树是二维数据数据结构,Linked List是特殊化的Tree,Tree是特殊化的Graph。前端工作中常见的树包括:DOM树、级联选择、树形控件...
级联选择组件
树形控件
JS中没有树,但是可以用Object和Array构建树
var tree = {
"lable": "根节点",
"value": 0,
"hasChildren": true,
"children": [
{
"lable": "父节点1",
"value": 1,
"hasChildren": true,
"children": [
{
"lable": "子节点11",
"value": 2,
"hasChildren": false
}, {
"lable": "子节点12",
"value": 3,
"hasChildren": false
}
]
}, {
"lable": "父节点2",
"value": 4,
"hasChildren": true,
"children": [
{
"lable": "子节点21",
"value": 5,
"hasChildren": false
},
{
"lable": "子节点22",
"value": 6,
"hasChildren": false
}
]
}
]
};
树的结构
function TreeNode(val){
this.val = val;
this.left = this.right = null;
}
树的常用操作:深度/广度优先遍历、先中后序遍历
深度与广度优先遍历
深度优先遍历:尽可能深的搜索树的分支。深度优先遍历算法:
- 1、访问根节点
- 2、对根节点的children挨个进行深度优先遍历
const dfs = (root) => {
console.log(root.lable);
root.hasChildren && root.children.forEach(dfs);
}
dfs(tree);
//输出结果
根节点
父节点1
子节点11
子节点12
父节点2
子节点21
子节点22
广度优先遍历:先访问离根节点最近的节点。广度优先遍历算法:
- 1、新建一个队列,把根节点入队
- 2、把队头出队并访问
- 3、把队头的children挨个入队
- 4、重复2、3步,直到队列为空
const bfs = (root) => {
let queue = [root];
while (queue.length) {
const n = queue.shift();
console.log(n.lable);
if (n.hasChildren){
n.children.forEach(child => queue.push(child))
}
}
}
bfs(tree);
//输出结果
根节点
父节点1
父节点2
子节点11
子节点12
子节点21
子节点22
先中后序遍历(二叉树)
二叉树中每个节点最多只能有两个子节点。遍历方法有:递归版和非递归版(先序遍历、中序遍历、后序遍历)
在JS中通常用Object来模拟二叉树
var binaryTree = {
label: '根节点',
value: 1,
left: {
label: '左节点',
value: 2,
left: {
label: '左节点-左',
value: 3,
left: null,
right: null,
},
right: {
label: '左节点-右',
value: 4,
left: {
label: '右节点-右-左',
value: 5,
left: null,
right: null,
},
right: null,
}
},
right: {
label: '右节点',
value: 6,
left: null,
right: {
label: '右节点-右',
value: 7,
left: null,
right: null,
},
}
}
先序遍历算法:
- 1、访问根节点
- 2、对根节点的左子树进行先序遍历
- 3、对根节点的右子树进行先序遍历
//递归版
const preorder = (root)=> {
if(!root){
return;
}
console.log(root.value);
preorder(root.left);
preorder(root.right);
}
//非递归版
const preorder2 = (root)=> {
if(!root){
return;
}
let stack = [root];
while (stack.length) {
let n = stack.pop();
console.log(n.value);
if (n.right) {
stack.push(n.right)
}
if(n.left){
stack.push(n.left)
}
}
}
preorder(binaryTree);
//输出结果
1
2
3
4
5
6
7
中序遍历算法:
- 1、对根节点的左子树进行中序遍历
- 2、访问根节点
- 3、对根节点的右子树进行中序遍历
//递归版
const inorder = (root)=> {
if(!root){
return;
}
inorder(root.left);
console.log(root.value);
inorder(root.right);
}
//非递归版
const inorder2 = (root)=> {
if(!root){ return; }
let stack = [];
let p = root;
while (stack.length || p) {
while (p) {
stack.push(p);
p = p.left;
}
const n = stack.pop();
console.log(n.value);
p = n.right;
}
}
inorder(binaryTree);
//输出结果
3
2
5
4
1
6
7
后序遍历算法:
- 1、对跟节点的左子树进行后序遍历
- 2、对根节点的右子树进行后序遍历
- 3、访问根节点
//递归版
const postorder = (root) => {
if(!root){
return;
}
postOrder(root.left);
postOrder(root.right);
console.log(root.value);
}
//非递归版
const postorder2 = (root) => {
if (!root) {
return;
}
let stack = [root];
let outputStack = [];
while (stack.length) {
const n = stack.pop();
outputStack.push(n);
if(n.left){
stack.push(n.left);
}
if (n.right) {
stack.push(n.right)
}
}
while (outputStack.length) {
const o = outputStack.pop();
console.log(o.value);
}
}
postorder(binaryTree);
//输出结果:
3
5
4
2
7
6
1
前端与树
1、遍历JSON的所有节点值
const json = {
a: { b: { c: 1}},
d: [1,2,3]
}
const dfs = (n, path) => {
console.log(n, path);
Object.keys(n).forEach((key)=> {
dfs(n[key], path.concat(key))
})
}
dfs(json, []);
2、渲染Antd的树组件