二叉树的 前、中、后 序遍历(JS版,两种方式:递归+迭代)

首先,来了解下3中遍历方式的区别:

【前序遍历】按照 根、左、右 的次序进行遍历

【中序遍历】按照 左、根、右 的次序进行遍历

【后序遍历】按照 左、右、根 的次序进行遍历

 

树节点的结构

/**
 * 单个树节点的结构
 * @param {number} val
 * @param {TreeNode} left
 * @param {TreeNode} right
 */
function TreeNode(val = 0, left = null, right = null) {
  this.val = val;
  this.left = left;
  this.right = right;
}

 

然后,我们再来熟悉一下二叉树这3种遍历的递归实现

1. 前序遍历

/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var preorderTraversal = function(root) {
  if (root === null) return [];
  // 按前序的次序来存储节点的值
  const ans = [];
  /**
   * 递归遍历树节点
   */
  const traversal = (node) => {
    // 如果该子节点不存在,则终止递归
    if (node === null) return;
    // 处理当前节点
    ans.push(node.val);
    // 处理当前节点的左子树
    traversal(node.left);
    // 处理当前节点的右子树
    traversal(node.right);
  };
  traversal(root);

  return ans;
};

2. 中序遍历

// 思路同上,只需将部分代码调整如下

// ...
    // 处理当前节点的左子树
    traversal(node.left);
    // 处理当前节点
    ans.push(node.val);
    // 处理当前节点的右子树
    traversal(node.right);
// ...

3. 后序遍历

// 思路同上,只需将部分代码调整如下

// ...
    // 处理当前节点的左子树
    traversal(node.left);
    // 处理当前节点的右子树
    traversal(node.right);
    // 处理当前节点
    ans.push(node.val);
// ...

 

 

遍历版本

前序

// 二叉树的前序遍历
var preorderTraversal = function(root) {
    if (root === null) return [];
    // 使用栈保存尚未读取值的树节点
    const stk = [root];
    // 保存遍历过后的节点值
    const ans = [];

    while (stk.length > 0) {
        // 推出栈顶节点,访问其值并处理其左右子节点
        const node = stk.pop();
        ans.push(node.val);
        if (node.right !== null) stk.push(node.right);
        if (node.left !== null) stk.push(node.left);
    }
    
    return ans;
};

 

中序

// 二叉树的中序遍历
var inorderTraversal = function(root) {
    if (root === null) return [];
    // 存放节点值
    const ans = [];
    // 存放未访问过的节点
    const stk = [];
    // 节点指针
    let cur = root;

    // 按照左 中 右遍历节点
    while (cur !== null || stk.length > 0) {
        // 先将当前轮次可以遍历到的左子节点推入栈
        while (cur !== null) {
            stk.push(cur);
            cur = cur.left;
        }
        // 取出栈顶节点
        const node = stk.pop();
        ans.push(node.val);
        // 如果存在右子节点,则相应地对其进行遍历
        if (node.right !== null) cur = node.right;
    }

    return ans;
};

 

后序(方式1)

// 后序遍历
var postorderTraversal = function(root) {
    if (root === null) return [];
    // 后序遍历次序是左右根
    // 前序遍历次序是根左右(尝试将其变化为 根右左 后进行反转)

    const stk = [root];
    const ans = [];
    while (stk.length > 0) {
        const node = stk.pop();
        ans.push(node.val)
        if (node.left !== null) stk.push(node.left);
        if (node.right !== null) stk.push(node.right);
    }

    return ans.reverse();
};

后序(方式2)

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var postorderTraversal = function(root) {
    if (!root) {
        return [];
    }
    const stk = [];
    const res = [];
    let prev = null;
    let cur = root;
    
    while (cur !== null || stk.length > 0) {
        // 优先深度查询左子树
        while (cur !== null) {
            stk.push(cur);
            cur = cur.left;
        }
        // 获取缓存的栈顶节点
        cur = stk.pop();
        // 如果当前节点的右子树不存在或者上一次操作的节点为该节点的右子节点
        if (!cur.right || cur.right === prev) {
            res.push(cur.val);
            prev = cur;
            cur = null;
        }
        // 如果右子树存在
        else {
            stk.push(cur);
            cur = cur.right;
        }
    }

    return res;
};

 

End

posted @ 2022-04-01 00:43  樊顺  阅读(476)  评论(0编辑  收藏  举报