一、树是什么?
一种分层数据的抽象模型
1.前端工作中常见的树包括: DOM树、级联选择、树形控件...
2.JS中没有树,但是可以用Object和Array构建树
{
value: "",
label: "",
children: [
{
value: "",
label: "",
children: []
}
]
}
3.树的常用操作:深度/广度优先遍历、先中后序遍历
二、什么是深度/广度优先遍历?
深度优先遍历:尽可能深的搜索树的分支
深度优先遍历流程
- 访问根节点
- 对根节点的children挨个进行深度优先遍历 (递归)
代码实现
const tree = {
val: "a",
children: [
{
val: "b",
children: [
{
val: "d",
children: [],
},
{
val: "e",
children: [],
},
],
},
{
val: "c",
children: [
{
val: "f",
children: [],
},
{
val: "g",
children: [],
},
],
},
],
};
//深度优先遍历
const dfs = (root) => {
console.log(root.val);
root.children.forEach((child) => {dfs(child)});
}
dfs(tree);
广度优先遍历:先访问离根节点最近的节点
广度优先遍历流程
- 新建一个队列,把根节点入队
- 把队头出队并访问
- 把队头的children挨个入队
- 重复第二、三步,直到队列为空
代码实现
const tree = {
val: "a",
children: [
{
val: "b",
children: [
{
val: "d",
children: [],
},
{
val: "e",
children: [],
},
],
},
{
val: "c",
children: [
{
val: "f",
children: [],
},
{
val: "g",
children: [],
},
],
},
],
};
//广度优先遍历
const bfs = (root) => {
const q = [root];
while(q.length > 0) {
const n = q.shift();
console.log(n.val);
n.children.forEach(child => {
q.push(child);
})
}
}
bfs(tree);
三、二叉树的先中后序遍历
二叉树定义
树中每个节点最多只能有两个子节点
在js中通常用Object来模拟二叉树
const binaryTree = {
val: 1,
left: {
val : 2,
left: null,
right: null
},
right: {
val: 3,
left: null,
right: null
}
}
先序遍历: 根左右
- 访问根节点
- 对根节点的左子树进行先序遍历
- 对根节点的右子树进行先序遍历
递归版代码实现
const bt = {
val: 50,
left: {
val: 10,
left: {
val: 5,
left: null,
right: null,
},
right: {
val: 15,
left: null,
right: null,
},
},
right: {
val: 70,
left: {
val: 60,
left: null,
right: null,
},
right: {
val: 80,
left: null,
right: null,
},
},
};
先序遍历
const preorder = (root) => {
if (!root) { return };
console.log(root.val);
preorder(root.left);
preorder(root.right);
}
// 50 10 5 15 70 60 80
preorder(bt);
非递归版代码实现
const bt = {
val: 50,
left: {
val: 10,
left: {
val: 5,
left: null,
right: null,
},
right: {
val: 15,
left: null,
right: null,
},
},
right: {
val: 70,
left: {
val: 60,
left: null,
right: null,
},
right: {
val: 80,
left: null,
right: null,
},
},
};
const preorder = (root) => {
if (!root) { return; }
const stack = [root];
while (stack.length) {
const n = stack.pop();
console.log(n.val);
if (n.right) stack.push(n.right);
if (n.left) stack.push(n.left);
}
};
// 50 10 5 15 70 60 80
preorder(bt);
中序遍历: 左根右
- 对根节点的左子树进行中序遍历
- 访问根节点
- 对根节点的右子树进行中序遍历
递归版代码实现
const bt = {
val: 56,
left: {
val: 22,
left: {
val: 10,
left: null,
right: null,
},
right: {
val: 30,
left: null,
right: null,
},
},
right: {
val: 81,
left: {
val: 77,
left: null,
right: null,
},
right: {
val: 92,
left: null,
right: null,
},
},
};
中序遍历
const inorder = (root) => {
if (!root) { return; }
inorder(root.left);
console.log(root.val);
inorder(root.right);
};
// 10 22 30 56 77 81 92
inorder(bt);
非递归版代码实现
const bt = {
val: 56,
left: {
val: 22,
left: {
val: 10,
left: null,
right: null,
},
right: {
val: 30,
left: null,
right: null,
},
},
right: {
val: 81,
left: {
val: 77,
left: null,
right: null,
},
right: {
val: 92,
left: null,
right: null,
},
},
};
const inorder = (root) => {
if (!root) { return; }
const stack = [];
let p = root;
while (stack.length || p) {
while (p) {
stack.push(p);
p = p.left;
}
const n = stack.pop();
console.log(n.val);
p = n.right;
}
};
// 10 22 30 56 77 81 92
inorder(bt);
后序遍历: 左右根
- 对根节点的左子树进行后序遍历
- 对根节点的右子树进行后序遍历
- 访问根节点
递归版代码实现
const bt = {
val: 23,
left: {
val: 16,
left: {
val: 3,
left: null,
right: null,
},
right: {
val: 22,
left: null,
right: null,
},
},
right: {
val: 45,
left: {
val: 37,
left: null,
right: null,
},
right: {
val: 99,
left: null,
right: null,
},
},
};
//后序遍历
const postorder = (root) => {
if (!root) { return; }
postorder(root.left);
postorder(root.right);
console.log(root.val);
};
// 3 22 16 37 99 45 23
postorder(bt);
非递归版代码实现
const bt = {
val: 23,
left: {
val: 16,
left: {
val: 3,
left: null,
right: null,
},
right: {
val: 22,
left: null,
right: null,
},
},
right: {
val: 45,
left: {
val: 37,
left: null,
right: null,
},
right: {
val: 99,
left: null,
right: null,
},
},
};
const postorder = (root) => {
if (!root) { return; }
const outputStack = [];
const stack = [root];
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 n = outputStack.pop();
console.log(n.val);
}
};
postorder(bt);