数据结构 - 二叉树

44 阅读3分钟

数据结构 --- 二叉树的遍历

二叉树的遍历

递归实现 先序 中序 后序遍历

递归的实现很简答,并且前中后共用一个模版。

先序遍历
var preorderTraversal = function(root) {
    let res = []
    if(root === null) return res
    function helper(root){
        if(root === null) return
        res.push(root.val)
        if(root.left) helper(root.left)
        if(root.right) helper(root.right)
    }
    helper(root)
    return res
};
中序遍历

顺序为:左 中 右

var inorderTraversal = function(root) {
    let res = []
    if(root === null) return res
    function helper(root){
        if(root === null) return
        if(root.left) helper(root.left) //这两行交换了顺序
        res.push(root.val) //这两行交换了顺序
        if(root.right) helper(root.right)
    }
    helper(root)
    return res
};
后序遍历

顺序为:左 右 中

var inorderTraversal = function(root) {
    let res = []
    if(root === null) return res
    function helper(root){
        if(root === null) return
        if(root.left) helper(root.left)
        if(root.right) helper(root.right)
        res.push(root.val)
    }
    helper(root)
    return res
};
递归三部曲
  • 规定参数和返回值
  • 确定终止条件
  • 确定单层递归的逻辑

二叉树的迭代遍历

我们都知道,递归虽代码量小,但不是一种很高效的解决问题的办法,严格的面试官有可能考验你使用迭代遍历二叉树问题。

迭代实现二叉树的遍历的精髓在于标记每个根节点(栈后面补一个null)这样就可以知道这个根节点有没有添加它的左右节点进栈,当发现被标记过的节点,把它放进结果数组即可。

先序遍历
var preorderTraversal = function(root) {
    let stack = []
    let res = []
    if(root === null) return res
    stack.push(root)
    while(stack.length){
        let node = stack.pop()
        if(node === null){
            res.push(stack.pop().val) //是被标记过的节点,直接加入res
            continue
        }
        else { //不是,把它的左右节点入栈,并把它自己标记入栈
            node.right && stack.push(node.right) //入栈顺序与结果是相反的
            node.left && stack.push(node.left)
            stack.push(node)
            stack.push(null)
        }
    }
    return res
};
中序遍历
var inorderTraversal = function(root) {
    let stack = []
    let res = []
    if(root === null) return res
    stack.push(root)
    while(stack.length){
        let node = stack.pop()
        if(node === null){
            res.push(stack.pop().val) //是被标记过的节点,直接加入res
            continue
        }
        else { //不是,把它的左右节点入栈,并把它自己标记入栈
            node.right && stack.push(node.right) //入栈顺序与结果是相反的
            stack.push(node)
            stack.push(null)
            node.left && stack.push(node.left)

        }
    }
    return res
};
后序遍历
var inorderTraversal = function(root) {
    let stack = []
    let res = []
    if(root === null) return res
    stack.push(root)
    while(stack.length){
        let node = stack.pop()
        if(node === null){
            res.push(stack.pop().val) //是被标记过的节点,直接加入res
            continue
        }
        else { //不是,把它的左右节点入栈,并把它自己标记入栈
            stack.push(node)
            stack.push(null)
            node.right && stack.push(node.right) //入栈顺序与结果是相反的
            node.left && stack.push(node.left)

        }
    }
    return res
};

二叉树的层次遍历

学会二叉树的层序遍历,可以一口气打完以下十题,10分钟leetcode刷题数+10(不是:

  • 102.二叉树的层序遍历
  • 107.二叉树的层次遍历II
  • 199.二叉树的右视图
  • 637.二叉树的层平均值
  • 429.N叉树的层序遍历
  • 515.在每个树行中找最大值
  • 116.填充每个节点的下一个右侧节点指针
  • 117.填充每个节点的下一个右侧节点指针II
  • 104.二叉树的最大深度
  • 111.二叉树的最小深度

二叉树的层次遍历

根据上面迭代的算法,我们其实可以想到,层次遍历不就是先输出左右节点,把左右节点的左右节点入队不就行了么,用一个队列即可实现啊

var levelOrder = function(root) {
    let queue = []
    let res = []
    if(root === null) return res
    queue.push(root)
    while(queue.length){
        let len = queue.length
        let curRes = [] //每一层的节点
        for(let i=0;i<len;i++){
            let node = queue.shift()
            curRes.push(node.val)
            node.left && queue.push(node.left)
            node.right && queue.push(node.right)
        }
        res.push(curRes)
    }
    return res
};