JavaScript二叉树相关概念

概述

二叉树是最基础并且及其常用的数据结构之一,对于前端开发来说,必须掌握相关概念,下文总结部分和二叉树相关的概念知识。

二叉树节点定义

js 复制代码
// 二叉树节点类
class TreeNode {
  constructor(val = 0, left = null, right = null) {
    this.val = val;     // 节点值
    this.left = left;   // 左子树
    this.right = right; // 右子树
  }
}

前序遍历

顺序:根 → 左 → 右

js 复制代码
/**
 * 前序遍历 - 递归版本
 * @param {TreeNode} root 根节点
 * @return {number[]} 遍历结果数组
 */
function preorderTraversal(root) {
  const result = [];
  
  function traverse(node) {
    if (!node) return;
    
    result.push(node.val);  // 先访问根节点
    traverse(node.left);    // 再遍历左子树
    traverse(node.right);   // 最后遍历右子树
  }
  
  traverse(root);
  return result;
}

/**
 * 前序遍历 - 迭代版本(使用栈)
 */
function preorderTraversalIterative(root) {
  const result = [];
  const stack = [];
  let current = root;
  
  while (current || stack.length) {
    // 不断向左深入,访问节点并压入栈
    while (current) {
      result.push(current.val);  // 访问当前节点
      stack.push(current);       // 压入栈,用于后续访问右子树
      current = current.left;    // 移动到左子节点
    }
    
    // 弹出栈顶节点,处理其右子树
    current = stack.pop();
    current = current.right;
  }
  
  return result;
}

中序遍历

顺序:左 → 根 → 右

js 复制代码
/**
 * 中序遍历 - 递归版本
 * @param {TreeNode} root 根节点
 * @return {number[]} 遍历结果数组
 */
function inorderTraversal(root) {
  const result = [];
  
  function traverse(node) {
    if (!node) return;
    
    traverse(node.left);    // 先遍历左子树
    result.push(node.val);  // 再访问根节点
    traverse(node.right);   // 最后遍历右子树
  }
  
  traverse(root);
  return result;
}

/**
 * 中序遍历 - 迭代版本
 */
function inorderTraversalIterative(root) {
  const result = [];
  const stack = [];
  let current = root;
  
  while (current || stack.length) {
    // 不断向左深入,将所有左子节点压入栈
    while (current) {
      stack.push(current);
      current = current.left;
    }
    
    // 弹出栈顶节点并访问
    current = stack.pop();
    result.push(current.val);
    
    // 处理右子树
    current = current.right;
  }
  
  return result;
}

后续遍历

顺序:左 → 右 → 根

js 复制代码
/**
 * 后序遍历 - 递归版本
 * @param {TreeNode} root 根节点
 * @return {number[]} 遍历结果数组
 */
function postorderTraversal(root) {
  const result = [];
  
  function traverse(node) {
    if (!node) return;
    
    traverse(node.left);    // 先遍历左子树
    traverse(node.right);   // 再遍历右子树
    result.push(node.val);  // 最后访问根节点
  }
  
  traverse(root);
  return result;
}

/**
 * 后序遍历 - 迭代版本(使用两个栈)
 */
function postorderTraversalIterative(root) {
  if (!root) return [];
  
  const result = [];
  const stack1 = [root];
  const stack2 = [];
  
  // 使用第一个栈进行类似前序遍历(根→右→左)
  while (stack1.length) {
    const node = stack1.pop();
    stack2.push(node);
    
    if (node.left) stack1.push(node.left);
    if (node.right) stack1.push(node.right);
  }
  
  // 第二个栈弹出顺序即为后序遍历结果(左→右→根)
  while (stack2.length) {
    result.push(stack2.pop().val);
  }
  
  return result;
}

根据遍历结果重构二叉树

1. 前序 + 中序 → 二叉树

javascript 复制代码
/**
 * 根据前序遍历和中序遍历结果构建二叉树
 * @param {number[]} preorder 前序遍历结果
 * @param {number[]} inorder 中序遍历结果
 * @return {TreeNode} 重构的二叉树根节点
 */
function buildTreeFromPreIn(preorder, inorder) {
  // 创建中序遍历值到索引的映射,便于快速查找根节点位置
  const inorderMap = new Map();
  for (let i = 0; i < inorder.length; i++) {
    inorderMap.set(inorder[i], i);
  }
  
  let preIndex = 0; // 前序遍历索引指针
  
  function build(left, right) {
    // 递归终止条件:左右边界交叉
    if (left > right) return null;
    
    // 前序遍历的第一个元素就是根节点
    const rootVal = preorder[preIndex++];
    const root = new TreeNode(rootVal);
    
    // 在中序遍历中找到根节点的位置
    const inorderIndex = inorderMap.get(rootVal);
    
    // 递归构建左子树和右子树
    // 左子树:中序遍历中根节点左边的部分
    root.left = build(left, inorderIndex - 1);
    // 右子树:中序遍历中根节点右边的部分
    root.right = build(inorderIndex + 1, right);
    
    return root;
  }
  
  return build(0, inorder.length - 1);
}

2. 中序 + 后序 → 二叉树

javascript 复制代码
/**
 * 根据中序遍历和后序遍历结果构建二叉树
 * @param {number[]} inorder 中序遍历结果
 * @param {number[]} postorder 后序遍历结果
 * @return {TreeNode} 重构的二叉树根节点
 */
function buildTreeFromInPost(inorder, postorder) {
  const inorderMap = new Map();
  for (let i = 0; i < inorder.length; i++) {
    inorderMap.set(inorder[i], i);
  }
  
  let postIndex = postorder.length - 1; // 后序遍历索引指针(从后往前)
  
  function build(left, right) {
    if (left > right) return null;
    
    // 后序遍历的最后一个元素是根节点
    const rootVal = postorder[postIndex--];
    const root = new TreeNode(rootVal);
    
    const inorderIndex = inorderMap.get(rootVal);
    
    // 注意:先构建右子树,再构建左子树(因为后序遍历是左右根,倒序是根右左)
    root.right = build(inorderIndex + 1, right);
    root.left = build(left, inorderIndex - 1);
    
    return root;
  }
  
  return build(0, inorder.length - 1);
}

测试示例

javascript 复制代码
// 创建测试二叉树
//       1
//      / \
//     2   3
//    / \
//   4   5
const root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);

// 测试遍历
console.log('前序遍历:', preorderTraversal(root));        // [1, 2, 4, 5, 3]
console.log('中序遍历:', inorderTraversal(root));         // [4, 2, 5, 1, 3]
console.log('后序遍历:', postorderTraversal(root));       // [4, 5, 2, 3, 1]

// 测试重构
const preorder = [1, 2, 4, 5, 3];
const inorder = [4, 2, 5, 1, 3];
const postorder = [4, 5, 2, 3, 1];

const rebuiltRoot1 = buildTreeFromPreIn(preorder, inorder);
const rebuiltRoot2 = buildTreeFromInPost(inorder, postorder);

console.log('重构后的前序遍历:', preorderTraversal(rebuiltRoot1)); // 应该与原前序一致
console.log('重构后的中序遍历:', inorderTraversal(rebuiltRoot2));  // 应该与原中序一致

关键要点

  1. 前序+中序:可以唯一确定一棵二叉树
  2. 中序+后序:可以唯一确定一棵二叉树
相关推荐
崔庆才丨静觅8 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅10 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊10 小时前
jwt介绍
前端
爱敲代码的小鱼10 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax