二叉树先中后遍历

By bobo 2021-12-26 18:50:08 文章分类:JavaScript

先序遍历

先序遍历算法口诀

  • 访问 根 节点
  • 对根节点的 左 子树进行先序遍历
  • 对根节点的 右 子树进行先序遍历

图示:


       1
     /   \
    2     3
   / \    / \
  4   5  6   7

const bt = require('./bt')
preorder(bt) // 1,2,4,5,3,6,7

迭代版本

/**
 * 先序遍历
 * 核心就是:根 - 左 - 右
 * @param {*} root 
 * @returns 
 */
function preorder(root) {
    if (!root) return
    
    // 1.访问 根 节点
    console.log(root.val)

    // 2.对根节点的 左 子树进行先序遍历
    preorder(root.left)

    // 3.对根节点的 右 子树进行先序遍历
    preorder(root.right)
}

非递归实现,迭代实现

/**
 * 先序遍历
 * 核心就是:根 - 左 - 右
 * @param {*} root 
 * @returns 
 */
function preorder(root) {
    // 用来暂存节点的栈
    const stack = []
    // 新建一个游标节点为根节点
    let node = root

    // 当遍历到最后一个几点时,若它的左、右子树都为空,并且栈也为空
    // 则只需要不同时满足这两点,都需要进入循环
    while (node != null || stack.length > 0) {
        // 若当前考查节点非空,则输出该节点的值
        // 由考查顺序可知,需要一直往左走
        while (node != null) {
            console.log(node.val)
            // 为了之后能找到该节点的右子树,暂存该节点
            stack.push(node)
            node = node.left
        }

        // 一直到左子树为空,则开始考虑右子树
        // 弹出栈顶元素,将游标等于该节点的右子树
        if (stack.length > 0) {
            node = stack.pop()
            node = node.right
        }
    }
}

中序遍历

中序遍历算法口诀

  • 对根节点的左子树进行中序遍历
  • 访问根节点
  • 对根节点的右子树进行中序遍历

图示


       1
     /   \
    2     3
   / \   / \
  4  5  6   7

const bt = require('./bt')
inorder(bt) // 4,2,5,1,6,3,7

迭代方案

/**
 * 中序遍历
 * 左-根-右
 * 
 * @param {*} root 
 * @returns 
 */
function inorder(root) {
    if (!root) return

    // 1.对根节点的左子树进行中序遍历
    inorder(root.left)

    // 2.访问根节点
    console.log(root.val)

    // 3.对根节点的右子树进行中序遍历
    inorder(root.right)
}

非递归实现,迭代实现

/**
 * 中序遍历
 * 左-根-右
 * 
 * @param {*} root 
 * @returns 
 */
function inorder(root) {
    if (!root) return

    const stack = []
    let node = root;

    while (node != null || stack.length > 0) {
        // 这个 while 的作用是把寻找最左叶子结点的过程中,途径的所有结点都记录下来
        while (node != null) {
            stack.push(node)
            // 1.对根节点的左子树进行中序遍历
            node = node.left
        }

        if (stack.length > 0) {
            node = stack.pop()
            // 2.访问节点
            console.log(node.val)
            // 3.对根节点的右子树进行中序遍历
            node = node.right
        }
    }
}

后序遍历

后序遍历算法口诀

  • 对根节点的左子树进行后序遍历
  • 对根节点的右子树进行后序遍历
  • 访问根节点

图示


       1
     /   \
    2     3
   / \   / \
  4  5  6   7

const bt = require('./bt')
postorder(bt) // 4,5,2,6,7,3,1

迭代版本

/**
 * 后序遍历
 * 左-右-根
 * 
 * @param {*} root 
 * @returns 
 */
function postorder(root) {
    if (!root) return

    // 1.对根节点的左子树进行后序遍历
    postorder(root.left)

    // 2.对根节点的右子树进行后序遍历
    postorder(root.right)

    // 3.访问根节点
    console.log(root.val)
}

非递归实现,迭代实现

/**
 * 后序遍历
 * 左-右-根
 * 
 * @param {*} root 
 * @returns 
 */
function postorder(root) {
    const stack = []
    let node = root
    let visit = root

    while (node != null || stack.length > 0) {
        while (node != null) {
            stack.push(node)
            node = node.left
        }

        // 查看当前栈顶元素
        node = stack[stack.length - 1]
        // 如果其右子树也为空,或者右子树已经访问
        // 则可以直接输出当前节点的值
        if (node.right == null || node.right === visit) {
            console.log(node.val)
            stack.pop()
            visit = node
            node = null
        } else {
            // 否则继续遍历右子树
            node = node.right
        }
    }
}