一、基础概念
1. 二叉树基础
树是典型的非线性数据结构,二叉树 是树结构中最常用的类型,规则为:每个节点最多拥有两个子节点,分别称为左子节点、右子节点,左右节点顺序不可颠倒。
1.1 节点存储结构
二叉树的核心存储单元为节点(TreeNode),每个节点包含三部分:
- 数据域:存储节点数据
- 左子节点引用:指向左孩子
- 右子节点引用:指向右孩子
1.2 辅助概念:Hash算法
Hash 算法可将任意类型的数据计算出唯一哈希值,本案例中借助哈希值大小作为二叉搜索树的排序依据,实现节点有序存放。
二、二叉搜索树(排序二叉树)实现
二叉搜索树(BST)属于二叉树的衍生结构,核心规则:
- 左子树所有节点哈希值 < 当前节点哈希值
- 右子树所有节点哈希值 ≥ 当前节点哈希值
- 左右子树同样遵循上述规则
下面结合源码补全、修正代码,并添加详细注释。
2.1 完整代码实现
java
package com06.dc.zyf0606;
// 二叉树节点类
public class TreeNode<E> {
E data; // 节点存储的数据
TreeNode<E> left; // 左子节点引用
TreeNode<E> right; // 右子节点引用
// 构造方法:初始化节点数据,左右子节点默认为null
public TreeNode(E data) {
this.data = data;
}
}
// 二叉搜索树主体类
class Tree<E> {
TreeNode<E> root; // 根节点 // 整棵树的入口
int size; // 树中节点总数量
// 添加节点方法:按照哈希值大小插入,构建二叉搜索树
public void add(E data) {
TreeNode<E> node = new TreeNode<>(data);
// 树为空,直接将新节点设为根节点
if (root == null) {
root = node;
size++;
return;
}
TreeNode<E> temp = root;
// 循环遍历寻找合适的插入位置
while (temp != null) {
// 对比哈希值:当前节点哈希值更大,新节点放左子树
if (temp.data.hashCode() > data.hashCode()) {
// 左子节点为空,直接插入
if (temp.left == null) {
temp.left = node;
size++;
break;
} else {
// 左子节点不为空,继续向左遍历
temp = temp.left;
}
} else {
// 新节点哈希值更大/相等,新节点放右子树
if (temp.right == null) {
temp.right = node;
size++;
break;
} else {
// 右子节点不为空,继续向右遍历
temp = temp.right;
}
}
}
}
// 删除节点(待实现):二叉搜索树删除需处理三种情况,难点为找右子树最小节点
public void remove(E data) {
// 核心思路:删除非叶子节点时,使用【右子树最小节点】替代当前节点
}
// 查询节点(待实现):根据数据查找对应节点
public void get(E data) {
}
// 递归遍历:后序遍历(左->右->根)
private void print(TreeNode<E> temp) {
// 递归终止条件:节点为空,直接返回
if (temp == null) {
return;
}
print(temp.left); // 递归遍历左子树
print(temp.right); // 递归遍历右子树
System.out.print(temp.data + " , "); // 打印当前节点数据
}
// 对外提供的遍历方法,入口为根节点
public void printTree() {
print(root);
}
// 主方法:测试二叉搜索树
public static void main(String[] args) {
// 测试数据数组
String[] arr = {
"hello3859",
"hello227",
"hello3327",
"hello7885",
"hello6953",
"hello7066",
"hello7678",
"hello8134",
"hello8844",
"hello9459"
};
Tree<String> tree = new Tree<>();
// 循环添加数据到二叉搜索树
for (int i = 0; i < arr.length; i++) {
tree.add(arr[i]);
}
// 遍历打印整棵树
tree.printTree();
}
}
2.2 代码补充说明
- 遍历方式 :代码中
print方法为后序遍历,二叉树还有前序(根→左→右)、中序(左→根→右)两种常用递归遍历; - 与Set集合关联 :二叉搜索树天然具备数据不重复、无序存储 的特点,和 Java 中
Set集合特性一致,部分 Set 底层基于二叉搜索树实现; - 分层存储思路 :可借助动态数组,将二叉树每一层节点单独存入一个数组,数组数量等于树的高度,实现层序存储与层序遍历。
三、常见树结构分类、特性与应用场景
1. 普通二叉树
特性
- 每个节点最多两个子节点,无排序规则;
- 节点分布随机,查询、增删效率不稳定;
- 分为满二叉树、完全二叉树两个特殊子类。
应用场景
- 表达层级关系:文件目录、组织机构、家谱等;
- 简单层级数据展示、图形界面的树形菜单。
2. 二叉搜索树(BST)
特性
- 遵循左小右大排序规则,基于哈希值/自定义比较器排序;
- 理想状态下查询、插入、删除时间复杂度为 O(log n);
- 极端情况会退化为单链表(如顺序插入数据),效率降至 O(n)。
应用场景
- 基础有序数据查找、排序;
- 早期部分有序集合、索引结构的底层实现。
3. 平衡二叉树(AVL树)
特性
- 在二叉搜索树基础上增加平衡条件:左右子树高度差绝对值不超过 1;
- 节点增删后会自动旋转调平,杜绝退化为链表的问题;
- 查询效率稳定 O(log n),但频繁旋转会增加增删开销。
应用场景
- 查询操作远多于增删操作的场景;
- 数据库部分索引、高频查询的有序数据结构。
4. 红黑树
特性
- 自平衡二叉搜索树,通过节点染色+旋转维持平衡;
- 平衡规则比 AVL 树宽松,旋转次数更少,增删效率更高;
- 最长路径不超过最短路径的 2 倍,查询效率稳定 O(log n)。
应用场景
- Java
TreeMap、TreeSet底层核心结构; - C++ STL 中的 map、set;
- Linux 内核进程调度、内存管理。
5. 堆(二叉堆)
特性
- 属于完全二叉树,分为大顶堆 和小顶堆;
- 大顶堆:父节点值 ≥ 所有子节点值;小顶堆:父节点值 ≤ 所有子节点值;
- 底层常用数组存储,不依赖指针。
应用场景
- 优先级队列(Java
PriorityQueue底层); - 堆排序算法、TopK 问题(找出最大/最小前 K 个元素);
- 任务优先级调度。
6. B树 / B+树(多路平衡查找树)
特性
- 多路平衡树,一个节点可包含多个子节点和多个数据,不再限制为二叉;
- 节点关键字有序,所有叶子节点处于同一层级;
- B+树所有数据仅存于叶子节点,非叶子节点只做索引。
应用场景
- 数据库索引(MySQL、Oracle 主流索引结构);
- 磁盘文件系统索引,适配磁盘块读写特性,减少 IO 次数。
7. 哈夫曼树(最优二叉树)
特性
- 带权路径长度最短的二叉树,又称最优二叉树;
- 只有度为 0 和 2 的节点,无单独左/右子节点;
- 根据权重构建,权重越大的节点离根节点越近。
应用场景
- 哈夫曼编码(数据压缩,如文本、图片压缩算法);
- 报文编码、传输加密场景。
8. 字典树(Trie树)
特性
- 又称前缀树,专门处理字符串;
- 公共前缀字符串共享节点,节点仅存储单个字符;
- 查询字符串前缀效率极高。
应用场景
- 输入法联想提示、单词前缀匹配;
- 搜索引擎关键词补全、字符串检索、敏感词过滤。
四、总结
- 二叉树是所有树形结构的基础,二叉搜索树引入排序规则,解决有序数据查找问题;
- 为优化二叉搜索树的缺陷,衍生出 AVL 树、红黑树等平衡树,适配不同读写场景;
- 多路树(B/B+树)主打磁盘存储与大数据索引,堆偏向优先级排序,字典树专攻字符串处理;
- 实际开发中根据数据类型、读写频率、存储介质选择对应树结构,是选型的核心依据。