RustBook项目中的二叉搜索树实现详解
2025-07-10 05:54:16作者:平淮齐Percy
二叉搜索树(Binary Search Tree, BST)是一种常见的数据结构,它能够高效地进行数据查找、插入和删除操作。本文将深入分析RustBook项目中提供的二叉搜索树实现,帮助读者理解其核心原理和Rust实现细节。
二叉搜索树基础结构
在RustBook项目中,二叉搜索树通过BST
结构体实现,其定义如下:
#[derive(Debug,Clone)]
struct BST<T,U> {
key: Option<T>,
val: Option<U>,
left: Link<T,U>,
right: Link<T,U>,
}
其中:
key
和val
分别存储节点的键和值,使用Option
类型表示可能为空left
和right
是子节点链接,类型为Link<T,U>
,即Option<Box<BST<T,U>>>
- 实现了
Debug
和Clone
trait,便于调试和复制
核心功能实现
1. 树的属性计算
项目实现了多种计算树属性的方法:
// 计算树的大小(节点总数)
fn size(&self) -> usize {
self.calc_size(0)
}
// 计算叶子节点数量
fn leaf_size(&self) -> usize {
if self.left.is_none() && self.right.is_none() {
return 1;
}
// 递归计算左右子树的叶子节点
// ...
}
// 计算非叶子节点数量
fn none_leaf_size(&self) -> usize {
self.size() - self.leaf_size()
}
// 计算树的深度
fn depth(&self) -> usize {
// 递归计算左右子树深度并取最大值
// ...
}
这些方法展示了递归在树结构计算中的典型应用,通过不断分解问题为更小的子问题来解决。
2. 插入操作
插入操作是BST的核心功能之一:
fn insert(&mut self, key: T, val: U) {
if self.key.is_none() {
// 空树直接插入
self.key = Some(key);
self.val = Some(val);
} else {
match &self.key {
Some(k) => {
if key == *k {
// 键已存在,更新值
self.val = Some(val);
return;
}
// 根据键大小决定插入左/右子树
let child = if key < *k {
&mut self.left
} else {
&mut self.right
};
// 递归插入
match child {
Some(ref mut node) => node.insert(key, val),
None => {
let mut node = BST::new();
node.insert(key, val);
*child = Some(Box::new(node));
},
}
},
None => (),
}
}
}
插入操作遵循BST的基本规则:小值放左边,大值放右边,相等则更新。
3. 查找操作
查找操作包括:
- 检查键是否存在(
contains
) - 获取键对应的值(
get
) - 查找最小/最大值(
min
/max
)
fn contains(&self, key: &T) -> bool {
match &self.key {
None => false,
Some(k) => match k.cmp(key) {
Equal => true,
Greater => self.left.as_ref().map_or(false, |n| n.contains(key)),
Less => self.right.as_ref().map_or(false, |n| n.contains(key)),
},
}
}
查找操作同样采用递归实现,利用BST的有序性快速定位目标。
树的遍历实现
项目实现了四种经典的树遍历方式:
1. 前序遍历(Preorder)
根节点 → 左子树 → 右子树
2. 中序遍历(Inorder)
左子树 → 根节点 → 右子树
对于BST,中序遍历会按升序输出所有键
3. 后序遍历(Postorder)
左子树 → 右子树 → 根节点
4. 层序遍历(Levelorder)
按层级从上到下、从左到右遍历
项目提供了两种实现方式:
- 内部方法:直接作为BST的方法
- 外部函数:接受BST节点作为参数
层序遍历使用队列辅助实现:
fn levelorder(&self) {
let size = self.size();
let mut q = Queue::new(size);
let _r = q.enqueue(Box::new(self.clone()));
while !q.is_empty() {
let front = q.dequeue().unwrap();
println!("key: {:?}, val: {:?}", front.key.unwrap(), front.val.unwrap());
// 将子节点加入队列
if let Some(left) = front.get_left() {
let _r = q.enqueue(left);
}
if let Some(right) = front.get_right() {
let _r = q.enqueue(right);
}
}
}
队列辅助结构
项目中定义了一个简单的队列用于层序遍历:
#[derive(Debug)]
struct Queue<T> {
cap: usize,
data: Vec<T>,
}
实现了基本的队列操作:入队(enqueue
)、出队(dequeue
)、检查空(is_empty
)等。
使用示例
项目提供了两个示例函数展示BST的使用:
fn basic() {
// 创建BST并插入数据
let mut bst = BST::<i32, char>::new();
bst.insert(8, 'e'); bst.insert(6,'c');
// ...更多插入操作
// 演示各种操作
println!("bst size: {}", bst.size());
println!("min key: {:?}", bst.min().0);
println!("contains 5: {}", bst.contains(&5));
}
fn order() {
// 创建BST
let mut bst = BST::<i32, char>::new();
// ...插入操作
// 演示各种遍历方式
println!("internal inorder:");
bst.inorder();
println!("external levelorder:");
let nk = Some(Box::new(bst.clone()));
levelorder(nk);
}
总结
RustBook项目中的BST实现展示了:
- 使用Rust实现经典数据结构的方法
- 递归在树操作中的广泛应用
- Rust所有权和借用系统在数据结构中的使用
- 泛型在数据结构实现中的应用
- 多种遍历算法的实现方式
这个实现涵盖了BST的核心功能,可以作为学习Rust数据结构的优秀范例。通过研究这段代码,开发者可以深入理解BST的工作原理和Rust语言特性在数据结构实现中的应用。