树
文章目录
- JS中没有树,但是可以用Object和Array构造树
- 树的常用操作:深度/广度优先遍历、先中后序遍历
树的深度与广度优先遍历
简单理解:
将 树 比作 书
深度遍历:从第一页看到最后一页
广度遍历:先看目录,再看内容
深度优先遍历
深度优先遍历的算法口诀:
- 访问根节点
- 对根节点的children挨个进行深度优先遍历
// 深层遍历
const tree = {
val:'a',
children:[
{
val:'b',
children:[
{
val:'c',
children:[]
}
]
},{
val:'d',
children:[
{
val:'e',
children:[]
}
]
}
]
}
const dfs = (root) => {
console.log(root.val)
root.children.forEach(children => dfs(children))
}
dfs(tree)
//> a
//> b
//> c
//> d
//> e
广度优先遍历
- 新建一个队列,把根节点入队
- 把队头出队并访问
- 把队头的children挨个入队
- 重复第二、三步,直到队列为空
const tree = {
val:'a',
children:[
{
val:'b',
children:[
{
val:'c',
children:[]
}
]
},{
val:'d',
children:[
{
val:'e',
children:[]
}
]
}
]
}
const bfs = (root) => {
const q = [root]
while(q.length > 0){
const n = q.shift()
console.log(n)
n.children.forEach(child => {
q.push(child)
})
}
}
bfs(tree)
二叉树
- 树中每个节点最多只能由两个子节点
- 在JS中通常用Object来模拟二叉树
先序遍历
- 访问根节点
- 对根节点的左子树进行先序遍历
- 对根节点的右子树进行先序遍历
const preorder = (root) => {
if (!root) return;
console.log(root.val)
preorder(root.left)
preorder(root.right)
}
中序遍历
- 对根节点的左子树进行中序遍历
- 访问根节点
- 对根节点的右子树进行中序遍历
const inorder = root => {
if(!root) return
inorder(root.left)
console.log(root.val)
inorder(root,right)
}
后序遍历
- 对根节点的左子树进行后序遍历
- 对根节点的右子树进行后序遍历
- 访问根节点
const postorder = root => {
if(!root) return
postorder(root.left)
postorder(root.right)
console.log(root.val)
}
非递归版的先中后遍历
主要是通过栈来实现,先进后出,后进先出
先序遍历
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)
}
}
中序遍历
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
}
}
后续遍历
后序遍历:左右根
先序遍历:根左右
思路:
将前序号排列逆向存储
const postorder = root => {
if(!root) return
const outputStack = []
const stack = []
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()
}
}
104 二叉树的最大深度
编写遍历树的函数——dfs()
参数:
- n:当前节点
- l:深度
dfs(root,1),传入的深度为1,因为只要根节点有值,那么深度最小就为1
var maxDepth = function(root) {
// 记录深度
let deep = 0
const dfs = (n,l) => {
if(!n)return
deep = Math.max(deep, l)
dfs(n.left,l+1)
dfs(n.right,l+1)
}
dfs(root,1)
return deep
};
111 二叉树的最小深度
- 求最小深度,考虑使用广度优先遍历
- 在广度优先遍历中,遇到叶子节点,停止遍历,返回节点层级
q中存储的是数组,[节点,层级]
var minDepth = function(root) {
if(!root) {return 0;}
const q = [[root,1]];
while(q.length){
const [n,l] = q.shift();
if(!n.left && !n.right){
return l;
}
if(n.left)q.push([n.left,l+1]);
if(n.right)q.push([n.right,l+1]);
}
};
102 二叉树的层序遍历
- 层序遍历顺序就是广度优先遍历
- 不过在遍历时候需要记录当前节点所在的层级,方便将其添加到不同的数组中
var levelOrder = function(root) {
if(!root) return []
let q = [[root,0]]
let global = []
while(q.length){
const [n,l] = q.shift()
if(!global[l]){
global.push([n.val])
}else{
global[l].push(n.val)
}
if(n.left) q.push([n.left,l+1])
if(n.right) q.push([n.right,l+1])
}
return global
};
前端与树:遍历SON的所有节点值
const json = {
a:{ b: { c: 1} },
d:[1,2]
}
const dfs = (n, path) => {
console.log(n,path)
Object.keys(n).forEach(k => {
dfs(n[k],path.concat(k))
})
}
dfs(json,)