树
exports.tree = {
val: 1,
children: [{
val: 2,
children: [{
val: 4,
children: []
},
{
val: 5,
children: []
}
]
},
{
val: 3,
children: [{
val: 6,
children: []
},
{
val: 7,
children: []
}
]
}
]
}
深度优先遍历
实现思路
1.访问根节点
2.对根节点的children挨个进行深度优先遍历
// 树深度优先遍历
const dfs = (root) => {
if (!root) return;
console.log(root.val);
root.children.forEach(dfs);
}
// 二叉树
const dfs = (root) => {
if (!root) return;
console.log(root.val);
dfs(root.left);
dfs(root.right);
}
广度优先遍历
实现思路
1.新建一个队列
2.把队头出队并访问
3.把队头children挨个入队
4.重复步骤2、3,直到队列为空
// 树广度优先遍历
const bfs = (root) => {
if (!root) return;
const q = [root];
while (q.length) {
const n = q.shift();
console.log(n.val);
n.children.forEach(child => {
q.push(child)
})
}
}
// 二叉树
const bfs = (root) => {
if (!root) return;
const q = [root];
while (q.length) {
const n = q.shift();
console.log(n.val);
if(n.left) q.push(n.left);
if(n.right) q.push(n.right);
}
}
二叉树
exports.bt = {
val: 1,
left: {
// 二级
val: 2,
left: {
val: 4,
left: null,
right: null
},
right: {
val: 5,
left: null,
right: null
},
},
right: {
// 二级
val: 3,
left: {
val: 6,
left: null,
right: null
},
right: {
val: 7,
left: null,
right: null
}
}
}
先序广度优先遍历
实现思路
1.访问根节点
2.对根节点的左子树进行先序遍历
3.对根节点的右子树进行先序遍历
// 先序广度优先遍历(递归版)
const preorder = (root) => {
if (!root) return;
console.log(root.val);
preorder(root.left);
preorder(root.right);
}
preorder(bt) // 1->2->4->5->3->6->7
// 先序广度优先遍历(非递归版)
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);
}
}
preorder(bt) // 1->2->4->5->3->6->7
中序广度优先遍历
实现思路
1.对根节点的左子树进行中序遍历
2.访问根节点
3.对根节点的右子树进行中序遍历
// 中序广度优先遍历(递归版)
const inorder = (root) => {
if (!root) return;
inorder(root.left);
console.log(root.val);
inorder(root.right);
}
inorder(bt) // 4->2->5->1->6->3->7
// 中序广度优先遍历(非递归版)
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;
}
}
inorder(bt) // 4->2->5->1->6->3->7
后序广度优先遍历
实现思路
1.对根节点的左子树进行中序遍历
2.对根节点的右子树进行中序遍历
3.访问根节点
// 后序广度优先遍历(递归版)
const inorder = (root) => {
if (!root) return;
inorder(root.left);
console.log(root.val);
inorder(root.right);
}
inorder(bt) // 4->5->2->6->7->3->1
// 后序广度优先遍历(非递归版)
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) // 4->5->2->6->7->3->1
翻转二叉树
const invertTree = function (root) {
// 定义递归边界
if (!root) {
return root;
}
// 递归交换右孩子的子结点
let right = invertTree(root.right);
// 递归交换左孩子的子结点
let left = invertTree(root.left);
// 交换当前遍历到的两个左右孩子结点
root.left = right;
root.right = left;
return root;
};
二叉搜索树(Binary Search Tree)简称 BST
查找数据域为某一特定值的结点
function search(root, val) {
// 若 root 为空,查找失败,直接返回
if(!root) {
return
}
// 找到目标结点,输出结点对象
if(root.val === val) {
console.log('目标结点是:', root)
} else if(root.val > val) {
// 当前结点数据域大于n,向左查找
search(root.left, val)
} else {
// 当前结点数据域小于n,向右查找
search(root.right, val)
}
}
插入新结点
function insertIntoBST(root, val) {
// 若 root 为空,说明当前是一个可以插入的空位
if(!root) {
// 用一个值为n的结点占据这个空位
root = new TreeNode(val)
return root
}
if(root.val > val) {
// 当前结点数据域大于n,向左查找
root.left = insertIntoBST(root.left, val)
} else {
// 当前结点数据域小于n,向右查找
root.right = insertIntoBST(root.right, val)
}
// 返回插入后二叉搜索树的根结点
return root
}
删除指定结点
function deleteNode(root, key) {
// 如果没找到目标结点,则直接返回
if (!root) {
return root
}
// 定位到目标结点,开始分情况处理删除动作
if (root.val === key) {
// 若是叶子结点,则不需要想太多,直接删除
if (!root.left && !root.right) {
root = null
} else if (root.left) {
// 寻找左子树里值最大的结点
const maxLeft = findMax(root.left)
// 用这个 maxLeft 覆盖掉需要删除的当前结点
root.val = maxLeft.val
// 覆盖动作会消耗掉原有的 maxLeft 结点
root.left = deleteNode(root.left, maxLeft.val)
} else {
// 寻找右子树里值最小的结点
const minRight = findMin(root.right)
// 用这个 minRight 覆盖掉需要删除的当前结点
root.val = minRight.val
// 覆盖动作会消耗掉原有的 minRight 结点
root.right = deleteNode(root.right, minRight.val)
}
} else if (root.val > key) {
// 若当前结点的值比 key 大,则在左子树中继续寻找目标结点
root.left = deleteNode(root.left, key)
} else {
// 若当前结点的值比 key 小,则在右子树中继续寻找目标结点
root.right = deleteNode(root.right, key)
}
return root
}
// 寻找左子树最大值
function findMax(root) {
while(root.right) {
root = root.right
}
return root
}
// 寻找右子树的最小值
function findMin(root) {
while(root.left) {
root = root.left
}
return root
}
应用
翻转二叉树
const invertTree = (root) => {
if (!root) {
return;
}
const left = invertTree(root.left);
const right = invertTree(root.right);
root.left = right;
root.right = left;
return root;
}