JS 二叉树入门:从概念到遍历,手把手教你吃透
作为 JS 初学者,提到二叉树可能会觉得头大,总觉得这是算法里的 “硬骨头”。但其实二叉树的核心逻辑特别简单,尤其是用 JS 来实现,只需要掌握对象和函数的基础就能上手。这篇文章会从二叉树的基本概念讲起,一步步带你实现二叉树的前序、中序、后序、层序四种遍历,全程代码落地,新手也能轻松看懂~
一、先搞懂:什么是树?什么是二叉树?
在学二叉树之前,我们先从最基础的树开始理解,它其实是对自然界树的抽象,和我们平时学的数组、对象这种线性结构不一样,树是非线性数据结构,核心是 “一个根,多分支”。
1. 树的核心概念(大白话版)
不用记学术定义,记住这几个关键词就行:
- 根节点:树的 “树根”,整棵树只有一个根节点,是所有节点的起点;
- 子节点 / 父节点:分支上的节点,上层是父节点,下层是子节点;
- 叶子节点:树的 “树叶”,也就是没有子节点的节点(度为 0 的节点);
- 度:一个节点有几个子分支,就是几度(比如有 2 个子节点,度为 2);
- 层次:根节点是第 1 层,根的子节点是第 2 层,依次往下数。
我用一张图来看一下一个二叉树模型
2. 二叉树:树的 “限定版”
二叉树是树中最常用的类型,它的规则特别简单,总结就一句话:要么空,要么有根 + 左子树 + 右子树,且左右子树也必须是二叉树。这里有两个关键注意点,新手很容易踩坑:
- 二叉树不是 “度为 2 的树”,节点的度可以是 0、1、2(比如叶子节点度为 0,只有左子节点的节点度为 1);
- 左右子树有严格顺序,不能随便交换,左就是左,右就是右。
3. 二叉树的节点结构
在 JS 里,二叉树的每个节点就是一个对象,核心包含三个部分:
- 数据域
val:节点存储的数值 / 内容; - 左节点引用
left:指向左子节点,没有就为null; - 右节点引用
right:指向右子节点,没有就为null。
这就是二叉树的最小单元,整棵二叉树就是由无数个这样的节点嵌套组合而成的。
二、JS 实现二叉树:两种创建方式
理解了节点结构,我们就能手动创建一棵二叉树了,JS 里有两种常用方式,对象字面量和构造函数,新手推荐先学对象字面量,更直观;构造函数适合后续动态创建节点。
方式 1:对象字面量(直观易上手)
直接用嵌套对象表示,从根节点开始,一层层往下写,没有子节点就写 null,一眼就能看清树的结构:
// 一棵简单的二叉树:根A,左子树B(子D、E),右子树C(子F)
const root = {
val: 'A',
left: {
val: 'B',
left: { val: 'D', left: null, right: null },
right: { val: 'E', left: null, right: null }
},
right: {
val: 'C',
left: null,
right: { val: 'F', left: null, right: null }
}
}
方式 2:构造函数(动态创建)
先写一个节点的构造函数,再通过实例化节点、设置左右引用,组装成树,适合需要动态添加 / 删除节点的场景:
// 二叉树节点构造函数
function TreeNode(val) {
this.val = val; // 数据域
this.left = null; // 左子节点默认null
this.right = null; // 右子节点默认null
}
// 实例化节点
const root = new TreeNode(1); // 根节点1
const node2 = new TreeNode(2); // 左子节点2
const node3 = new TreeNode(3); // 右子节点3
// 组装树:设置根节点的左右子节点
root.left = node2;
root.right = node3;
// 给node2添加左右子节点
node2.left = new TreeNode(4);
node2.right = new TreeNode(5);
可视化树结构
这里放一张二叉树模型图,对应上面的代码,能更直观地看到节点的层级和左右关系:
三、二叉树的核心:遍历!(递归版)
遍历就是按一定顺序访问二叉树的所有节点,且每个节点只访问一次。二叉树的遍历是面试和开发中的核心考点,其中前序、中序、后序是最基础的三种,这三种都可以用递归实现,而递归的逻辑和二叉树的定义高度契合,新手只要掌握规律,三分钟就能写出来。
1. 先搞懂:递归遍历的核心规律
二叉树的递归遍历,左右子树的访问顺序永远是左→右,唯一的区别是根节点的访问时机,这也是前 / 中 / 后序的命名由来:
- 前序:根节点 → 左子树 → 右子树(根在最前)
- 中序:左子树 → 根节点 → 右子树(根在中间)
- 后序:左子树 → 右子树 → 根节点(根在最后)
递归的两个核心要点,缺一不可:
- 递归退出条件:当节点为
null时,直接返回(没有子节点,无需遍历); - 重复执行逻辑:按照对应顺序,访问根节点、递归左子树、递归右子树。
2. 前序遍历(根→左→右)
实现代码
// 前序遍历:递归版
function preorder(root) {
// 退出条件:节点为null,直接返回
if (!root) return;
// 1. 先访问根节点
console.log(root.val);
// 2. 递归遍历左子树
preorder(root.left);
// 3. 递归遍历右子树
preorder(root.right);
}
// 调用:传入根节点,即可按顺序打印节点
preorder(root); // 输出:A B D E C F(对应上面的对象字面量树)
遍历过程可视化
这里放一张前序遍历过程图,一步步展示根节点的访问和左右子树的递归顺序,后面的中序、后序遍历都于此差不多:
我就先到遍历到B,后面的遍历过程都和这差不多,大家可以以此类推
3. 中序遍历(左→根→右)
只需要把根节点的访问语句移到左右子树递归之间,其他代码完全不变,新手可以直接复制改位置~
实现代码
// 中序遍历:递归版
function inorder(root) {
if (!root) return;
// 1. 先递归遍历左子树
inorder(root.left);
// 2. 再访问根节点
console.log(root.val);
// 3. 最后递归遍历右子树
inorder(root.right);
}
inorder(root); // 输出:D B E A C F
4. 后序遍历(左→右→根)
同理,把根节点的访问语句移到左右子树递归之后即可。
实现代码
// 后序遍历:递归版
function postorder(root) {
if (!root) return;
// 1. 递归遍历左子树
postorder(root.left);
// 2. 递归遍历右子树
postorder(root.right);
// 3. 最后访问根节点
console.log(root.val);
}
postorder(root); // 输出:D E B F C A
小总结
递归版的前 / 中 / 后序遍历,代码几乎一模一样,唯一的区别就是console.log(root.val)的位置,记住这个规律,再也不用死记硬背了!
四、层序遍历:按 “层” 走,迭代版(队列实现)
除了前 / 中 / 后序的深度优先遍历,还有一种常用的广度优先遍历—— 层序遍历,也就是从上到下、从左到右,按层级访问所有节点(比如根节点第 1 层,子节点第 2 层,依次往下)。
层序遍历不用递归,而是用队列来实现,核心思路是先进先出:把每一层的节点入队,访问完后再把其子节点入队,直到队列为空。
实现代码
// 层序遍历:迭代版(队列实现)
function levelOrder(root) {
// 空树直接返回空数组
if (!root) return [];
const result = []; // 存储遍历结果
const queue = [root]; // 初始化队列,入队根节点
// 队列不为空时,持续遍历
while (queue.length) {
const node = queue.shift(); // 队头出队(访问当前节点)
result.push(node.val); // 把节点值存入结果
// 左子节点存在,入队
if (node.left) queue.push(node.left);
// 右子节点存在,入队
if (node.right) queue.push(node.right);
}
return result; // 返回遍历结果
}
// 调用
console.log(levelOrder(root)); // 输出:['A','B','C','D','E','F']
层序遍历核心逻辑
- 初始化队列,将根节点入队,这是第 1 层;
- 出队队头节点,访问它并存入结果;
- 把该节点的左、右子节点依次入队(这是下一层的节点);
- 重复步骤 2-3,直到队列为空,所有层级都访问完毕。
五、新手必看:常见问题 & 小技巧
- 递归看不懂? :把递归看成 “重复执行相同的小事”,不用纠结递归的深层调用过程,只要写对退出条件和单次执行逻辑就行,计算机会自己处理嵌套;
- 树的结构看不清? :写代码时可以把树的结构按层级缩进,或者画一张简易的树图,对应代码来看;
- 遍历结果验证? :先手动按顺序数一遍节点,再运行代码对比,快速排查问题;
- 为什么层序用队列? :因为层序需要 “先进先出”,而队列的特性正好匹配;如果用栈,就会变成深度优先遍历了。
六、最后总结
二叉树其实是 JS 初学者入门算法的绝佳选择,核心内容就这几点,吃透了就能举一反三:
- 二叉树的节点是嵌套对象,核心包含
val、left、right; - 前 / 中 / 后序遍历(递归):左右顺序固定,根节点时机不同;
- 层序遍历(迭代):借助队列,按层级从上到下、从左到右访问;
- 递归的关键:退出条件 + 重复逻辑,不要纠结深层调用过程。
这篇文章的代码都是最基础的实现,新手可以直接复制到浏览器控制台 / VS Code 里运行,结合图片理解遍历过程。后续可以在此基础上拓展,比如遍历结果存入数组(而非打印)、二叉树的增删改查、二叉搜索树等内容,一步一个脚印,算法其实一点都不难~
最后:如果觉得这篇文章有用,欢迎点赞收藏~ 后续会继续更新 JS 算法的入门内容,从基础到进阶,和大家一起打怪升级~