二叉树及其遍历方式详解
在数据结构中,树是一种重要的非线性结构,而二叉树作为树的一种特殊形式,因其结构清晰、操作便捷而被广泛应用。本文将结合具体代码,详细介绍二叉树的基本概念及常见遍历方式。
二叉树的基本概念
二叉树是一种递归定义的数据结构,它具有以下特点:
- 可以是空树(没有任何节点)
- 若非空,则由根节点、左子树和右子树组成,且左右子树也都是二叉树
- 左右子树有严格的顺序,不能随意交换
二叉树的核心概念还包括:
- 层次:根节点位于第一层,其子女位于第二层,以此类推
- 高度:从叶子节点开始计算,逐层向上累加
- 度:一个节点拥有的子树数量
- 叶子节点:度为 0 的节点(没有子节点)
二叉树的节点定义
在 JavaScript 中,我们通常使用构造函数来定义二叉树的节点:
class TreeNode {
constructor(val) {
this.val = val; // 节点的值
this.left = this.right = null; // 左右子节点的引用,初始为null
}
}
使用该构造函数,我们可以创建一个二叉树实例:
// 创建节点
const root = new TreeNode(1);
const node2 = new TreeNode(2);
const node3 = new TreeNode(3);
// 构建树结构
root.left = node2;
root.right = node3;
也可以使用对象字面量的方式直接表示二叉树:
let tree = {
val: 1,
left: {
val: 2,
left: { val: 4, left: null, right: null },
right: { val: 5, left: null, right: null }
},
right: { val: 3, left: null, right: null }
};
二叉树的遍历方式
遍历是二叉树最基本的操作,指按某种顺序访问树中的所有节点,且每个节点仅被访问一次。常见的二叉树遍历方式有四种:前序遍历、中序遍历、后序遍历和层序遍历。
1. 前序遍历
前序遍历的顺序是:根节点 → 左子树 → 右子树。采用递归实现非常简洁:
function preorder(root) {
if (!root) {
return;
}
console.log(root.val); // 先访问根节点
preorder(root.left); // 再遍历左子树
preorder(root.right); // 最后遍历右子树
}
2. 中序遍历
中序遍历的顺序是:左子树 → 根节点 → 右子树:
function inorder(root) {
if (!root) {
return;
}
inorder(root.left); // 先遍历左子树
console.log(root.val); // 再访问根节点
inorder(root.right); // 最后遍历右子树
}
3. 后序遍历
后序遍历的顺序是:左子树 → 右子树 → 根节点:
function postorder(root) {
if (!root) {
return;
}
postorder(root.left); // 先遍历左子树
postorder(root.right); // 再遍历右子树
console.log(root.val); // 最后访问根节点
}
4. 层序遍历
层序遍历(也叫广度优先遍历)与上述三种深度优先遍历不同,它是按照树的层次从上到下、每层从左到右的顺序访问节点。实现层序遍历通常需要借助队列:
function levelOrder(root) {
if (!root) return []; // 空树返回空数组
const result = [];
const queue = [root]; // 初始化队列,将根节点入队
while (queue.length) { // 队列不为空时循环
const node = queue.shift(); // 出队一个节点(FIFO原则)
result.push(node.val); // 访问节点值
// 将左右子节点入队(如果存在)
if (node.left) {
queue.push(node.left);
}
if (node.right) {
queue.push(node.right);
}
}
return result;
}
总结
二叉树的遍历是处理树结构问题的基础,四种遍历方式各有特点:
- 前序、中序、后序遍历基于递归实现,本质上是深度优先搜索
- 层序遍历基于队列实现,本质上是广度优先搜索