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. 中序+后序:可以唯一确定一棵二叉树
相关推荐
望获linux10 分钟前
【Linux基础知识系列:第一百五十九篇】磁盘健康监测:smartctl
linux·前端·数据库·chrome·python·操作系统·软件
十一吖i1 小时前
vue3表格显示隐藏列全屏拖动功能
前端·javascript·vue.js
冰暮流星3 小时前
css之线性渐变
前端·css
徐同保3 小时前
tailwindcss暗色主题切换
开发语言·前端·javascript
mapbar_front3 小时前
大厂精英为何在中小公司水土不服?
前端
生莫甲鲁浪戴3 小时前
Android Studio新手开发第二十七天
前端·javascript·android studio
2501_916008895 小时前
Web 前端开发常用工具推荐与团队实践分享
android·前端·ios·小程序·uni-app·iphone·webview
SkylerHu6 小时前
前端代码规范:husky+ lint-staged+pre-commit
前端·代码规范
菜鸟una6 小时前
【微信小程序 + 消息订阅 + 授权】 微信小程序实现消息订阅流程介绍,代码示例(仅前端)
前端·vue.js·微信小程序·小程序·typescript·taro·1024程序员节
Yeats_Liao6 小时前
Go Web 编程快速入门 05 - 表单处理:urlencoded 与 multipart
前端·golang·iphone