二叉树遍历算法详解

91 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第29天,点击查看活动详情

二叉树由根节点、左子树、右子树构成。它有三种遍历方式——前序遍历,中序遍历和后序遍历,他们的遍历顺序分别为:

  • 前序遍历:根节点=>左子树=>右子树
  • 中序遍历:左子树=>根节点=>右子树
  • 后序遍历:左子树=>右子树=>根节点

这些遍历都可以用两种算法来解决,那就是递归和迭代。

144. 二叉树的前序遍历

  • 根据前序遍历的定义,每一次递归,会先打印根节点,再遍历左子树,再遍历右子树
var preorderTraversal = function(root) {
   let arr = []
   var getFun = function (root) {
       if (!root) return
       arr.push(root.val)
       root.left && getFun(root.left)
       root.right && getFun(root.right)
   }
   getFun(root)
   return arr
};

image.png

  • 对于迭代法,需要用到栈的数据结构,因其后进先出的特性,打印当前根节点后,需要先往栈中压入右节点,然后再遍历左节点
var preorderTraversal = function(root) {
   let res = []
   let stack = [root]
   while(stack.length) {
       let node = stack.pop()
       while (node) {
           res.push(node.val)
           stack.push(node.right)
           node = node.left
       }
       node && node.right && stack.push(node.right)
   }
   return res
};

image.png

这两种方案的时间复杂度都是O(n),n为节点个数,因为每个节点都会被遍历一次,空间复杂度为O(n),分别为递归过程中和迭代过程中栈的开销

94. 二叉树的中序遍历

  • 根据中序遍历的定义,每一次递归,会先遍历左子树,再打印根节点,再遍历右子树

代码如下

var inorderTraversal = function(root) {
    let res = []
    var getFun = function (root) {
        if (!root) return
        root.left && getFun(root.left)
        res.push(root.val)
        root.right && getFun(root.right)
    }
    getFun(root)
    return res
};
  • 同样,中序遍历也有迭代的算法
var inorderTraversal = function(root) {
    let res = []
    let stack = []
    while (root || stack.length) {
        // 先把所有左边界压入栈中,然后一个个弹出
        while(root) {
            stack.push(root)
            root = root.left
        }
        root = stack.pop()
        res.push(root.val)
        root = root.right
    }
    return res
};

时间和空间复杂度均和前序遍历相同

145. 二叉树的后序遍历

  • 根据后序遍历的定义,每一次递归,会先遍历左子树,再遍历右子树,再打印根节点
var postorderTraversal = function(root) {
   let res = []
   var getFun = function(root) {
       if (!root) return
       root.left && getFun(root.left)
       root.right && getFun(root.right)
       res.push(root.val)
   }
   getFun(root)
   return res
};
  • 递归属于正向思维,而迭代属于反向思维
  • 我们思考中序遍历,左右中,其实就是中右左的反向,那么我们只需要改动一下前序遍历成,然后反转数组即可
var postorderTraversal = function(root) {
   let res = []
   let stack = []
   while (root || stack.length) {
       while (root) {
         res.push(root.val)
         stack.push(root)
         root = root.right
       }
       root = stack.pop()
       root = root.left
   }
   res.reverse()
   return res
};