[路飞]二叉树的前序遍历、中序遍历、后序遍历

1,396 阅读3分钟

三种常见的二叉树遍历方法

  1. 前序遍历
    • 遍历方式:根->左->右,先输出根节点,再前序遍历左节点,再前序遍历右节点
  2. 中序遍历
    • 遍历方式:左->根->右,先中序遍历左节点,再输出根节点,再中序右节点
  3. 后序遍历
    • 遍历方式:左->右->根,先后序左节点,再后序右节点,再输出根节点

给个列子

二叉树节点.001.jpeg

  • 前序遍历的结果是:[1, 2, 4, 7, 5, 3, 6]
  • 中序遍历的结果是:[7, 3, 2, 5, 1, 3, 6]
  • 后序遍历的结果是:[7, 4, 5, 2, 6, 3, 1]

根据遍历的方式:可以看出递归的方式很容易的得到遍历的结果

递归

前序遍历

function preorder(root, ret = []) {
  // 边界情况
  if(root === null) return ret
  // 先输出根节点
  ret.push(root.val)
  // 前序遍历左节点
  preorder(root.left, ret)
  // 前序遍历右节点
  preorder(root.right, ret)
  return ret
}

中序遍历

function inorder(root, ret = []) {
    if (root === null) return ret
    // 中序遍历左节点
    inorder(root.left, ret)
    // 输出根节点
    ret.push(root.val)
    // 中序遍历右节点
    inorder(root.right, ret)
    return ret
}

后序遍历

function postorder(root, ret = []) {
    if (root === null) return ret
    // 后序遍历左节点
    postorder(root.left, ret)
    // 后序遍历右节点
    postorder(root.right, ret)
    // 输出根节点
    ret.push(root.val)
    return ret
}

进阶: 迭代

迭代的方式

前序遍历

function preorder(root) {
    if (root === null) return []
    const ret = []
    const stack = []
    stack.push(root)
    
    while(stack.length) {
       // 先把根节点取出来
       const node = stack.pop()
       // 输出根节点
       ret.push(node.val)
       // 这里每次我们pop出,我们要先获取输出left, 因此需要先push right 再push left
       if(node.right) stack.push(node.right)
       if(node.left) stack.push(node.left)
    }
    
    return ret
}

中序遍历

一开始前序迭代的解法比较容易,结果试了下中序,卡住了,特意做了下动画演示下我们的流程

动画做的有点问题, 右节点的时候同样是入栈,动画没有修改过来,就放到旁边了

中序遍历-循环.gif

文字解析

  • 我们需要的遍历的顺序是左 -> 根 -> 右, 所以如果我们的先深度找到最左的节点的节点
  • 按照中序遍历输出,我们就要记录遍历过程中的根节点
  • 输出完当前根节点,如果有右节点,我们再去把右节点迭代放入我们的栈中, 再依次输出

代码

// root 遍历过程中是当前节点,也可以用一个新的curNode来替代
function inorder(root) {
    const ret = []
    const stack = []
 
    while(stack.length || root) {
        // 先遍历到**最左**节点
        while(root) {
            stack.push(root)
            root = root.left
        }
        // 输出我们单前的左节点
        root = stack.pop()
        ret.push(root.val)
        // 再 去指向我们的右节点,遍历入栈, 如果没有右节点会pop出上一层的节点
        root = root.right
    }
    
    return ret
}

后序遍历

  • 和中序遍历的大致一样
  • 区分点: 当当前节点存在right的时候, 先把当前节点的文本域入栈(要不会出现死循环), 然后是当前节点指向right,进行后序遍历
  const stack = []
  const ret = []
 
  while (root || stack.length) {
    while (root) {
      stack.push(root)
      root = root.left
    }
    root = stack.pop()
    if (root.right) {
      stack.push({ val: root.val })
    } else {
      ret.push(root.val)
    }
    root = root.right
  }
  return ret

总结

  • 递归:按照遍历的顺序比较简单

  • 迭代:中序遍历 需要对当前节点进行一个过滤从而你达到先输出的节点,

  • 后序遍历: 在存在right的时候需要先把单前节点入栈,在对右节点进行遍历

  • 制作动画消耗了很长时间