二叉树学习及常见二叉树算法讲解

181 阅读2分钟

1.什么是二叉树

1.1直观表示如图所示,如树的枝叶式发散,有根节点,有叶子节点

二叉树.png

1.2 用js代码表示
const tree = {
    val:7,
    left:{
        val:4,
        left:{
            val:1
        },
        right:{
            val:3,
            left:{
                val:2
            }
        }
    },
    right:{
        val:6,
        right:{
            val:5
        }
    }
}

2.深度遍历/广度遍历

2.1.1什么是深度遍历

尽可能深的搜索树的分支

2.1.2代码实现深度遍历

”尽可能深“ 我们可以联想到递归,不断往内访问 就以上面js定义的二叉树结构为入参,来实现对该入参的深度遍历

function dfs(tree){
    if(!tree) return
    console.log(tree.val);
    if(tree.left) dfs(tree.left)
    if(tree.right) dfs(tree.right)
}
dfs(tree) // 7 4 1 3 2 6 5
2.2.1什么是广度遍历

优先访问离根节点最近的节点

2.2.2代码实现广度遍历
function bfs(tree){
    if(!tree) return
    const stack = [tree]
    while (stack.length) {
        const head = stack.shift()
        console.log(head.val);
        if (head.left) stack.push(head.left)
        if(head.right) stack.push(head.right)
    }
}
bfs(tree) // 7 4 6 1 3 5 2

3.先中后序遍历

以下代码实现均选1.2中的树结构作为入参参考实现。重点关注实现思想

3.1.1什么是先序遍历

先序遍历: 1.访问根节点 2.对根节点的左子树进行先序遍历 3.对根节点的右子树进行先序遍历 总结就是访问顺序:根->左->右

3.1.2代码实现(递归及非递归版)
// 递归实现
function preorder(tree){
    if(!tree) return
    console.log(tree.val);
    if(tree.left) preorder(tree.left)
    if(tree.right) preorder(tree.right)
}

// 非递归版
function preorder(tree){
    if(!tree) return
    const stack = [ tree ]
    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)
    }
}

3.2.1什么是中序遍历

中序遍历: 1.对根节点的左子树进行中序遍历 2.访问根节点 3.对根节点的右子树进行中序遍历 总结就是访问顺序:左->根->右

3.2.2代码实现(递归及非递归版)
// 递归版
function inorder(tree){
    if(!tree) return
    if(tree.left) inorder(tree.left)
    console.log(tree.val);
    if(tree.right) inorder(tree.right)
}

// 非递归版
function inorder(tree){
    if(!tree) return
    const stack = [] // 执行栈
    let p = tree // 全局指针
    while(stack.length || p){
        while(p){
            stack.push(p)
            p = p.left
        }
        const n = stack.pop()
        console.log(n.val);
        p = n.right
    }
}
3.3.1什么是后序遍历

后序遍历: 1.对左子树进行后序遍历 2.对右子树进行后序遍历 3.访问根节点 总结:左->右->根

3.3.2代码实现(递归及非递归版)
// 递归版
function postorder(tree){
    if(!tree) return
    if(tree.left) postorder(tree.left)
    if(tree.right) postorder(tree.right)
    console.log(tree.val);
}

// 非递归版
function postorder(tree){
    if(!tree) return
    const stack = [tree]
    const outStack = []
    while (stack.length) {
        const n = stack.pop()
        outStack.push(n)
        if(n.left) stack.push(n.left)
        if(n.right) stack.push(n.right)
    }
    while(outStack.length){
        const m = outStack.pop()
        console.log(m.val);
    }
}
postorder(tree)

4.求二叉树最大深度

二叉树.png

如图所示,最大深度为4,访问路径是:7->4->3->2

代码实现:
function maxDepth(tree){
    let res = 0
    function dfs(root,level){ // 深度遍历
        // level标记当前所处的层级
        if(!root.left && !root.right){
            // 只有叶子节点才进行计算,提高运算速度
            res = Math.max(res,level)
        }
        if(root.left) dfs(root.left,level+1)
        if(root.right) dfs(root.right,level+1)
    }
    dfs(tree,1)
    return res
}

5.求二叉树路径之和

如下图所示的路径有三条,各路径之和(从左到右)分别为: 12 16 18

二叉树.png

代码实现:
function depthSum(tree){
    if(!tree) return
    function dfs(root,sum){
        if(!root.left && !root.right){
            console.log(sum);
        }
        if(root.left) dfs(root.left,sum + root.left.val)
        if(root.right) dfs(root.right,sum + root.right.val)
    }
    dfs(tree,tree.val)
}