在学习了 Vec、HashMap、HashSet 之后,本节介绍 Rust 标准库中的其他重要集合类型:BTreeMap/BTreeSet (有序映射/集合)、VecDeque (双端队列)、BinaryHeap(优先队列/堆),以及它们的适用场景与性能特点。
1. BTreeMap 与 BTreeSet:有序数据结构
1.1 BTreeMap:基于 B 树的有序映射
BTreeMap<K, V> 提供按键排序的键值对存储,所有键都是有序的。
rust
use std::collections::BTreeMap;
fn main() {
// 创建一个空的 BTreeMap
let mut map = BTreeMap::new();
// 向 BTreeMap 中插入键值对
map.insert(3, "three");
map.insert(1, "one");
map.insert(2, "two");
// 1. 演示自动按键排序的遍历输出
println!("开始遍历 BTreeMap(自动按键排序):");
for (k, v) in &map {
println!("键: {}, 值: {}", k, v);
}
// 预期输出(因 BTreeMap 会按键升序排列):
// 键: 1, 值: one
// 键: 2, 值: two
// 键: 3, 值: three
// 2. 演示范围查询(range(2..=3))并输出结果
println!("\n开始范围查询(键在 2 到 3 之间,包含两端):");
let range: Vec<_> = map.range(2..=3).collect();
for (idx, (k, v)) in range.iter().enumerate() {
println!("第 {} 个匹配项 - 键: {}, 值: {}", idx + 1, k, v);
}
// 预期 range 内容(因按序排列,会匹配键 2 和 3):
// [(2, "two"), (3, "three")]
}

与 HashMap 的对比:
| 特性 | HashMap | BTreeMap |
|---|---|---|
| 顺序 | 无序 | 有序(按键排序) |
| 查找 | O(1) 平均 | O(log n) |
| 插入 | O(1) 平均 | O(log n) |
| 范围查询 | ❌ 不支持 | ✅ 支持 |
| 内存开销 | 较大(哈希表) | 较小(B树) |
| 适用场景 | 快速查找、无序访问 | 需要排序、范围查询 |
1.2 BTreeSet:有序集合
rust
use std::collections::BTreeSet;
fn main() {
// 1. 创建 BTreeSet 并插入元素
let mut set = BTreeSet::new();
set.insert(5);
set.insert(1);
set.insert(3);
println!("=== BTreeSet 自动排序遍历 ===");
// 2. 演示自动排序的遍历输出
for val in &set {
println!("遍历值: {}", val);
}
// 预期输出顺序:1 → 3 → 5(因 BTreeSet 按升序维护元素)
println!("\n=== 查找最小值和最大值 ===");
// 3. 查找并输出最小值
if let Some(min) = set.first() {
println!("最小值: {}", min);
} else {
println!("集合为空,无最小值");
}
// 4. 查找并输出最大值
if let Some(max) = set.last() {
println!("最大值: {}", max);
} else {
println!("集合为空,无最大值");
}
}

1.3 范围查询与迭代器
rust
use std::collections::BTreeMap;
fn main() {
// 创建 BTreeMap 存储学生分数(自动按键的字典序排序)
let mut scores = BTreeMap::new();
scores.insert("Alice", 95);
scores.insert("Bob", 87);
scores.insert("Charlie", 92);
scores.insert("David", 78);
// 1. 输出所有学生的分数(验证BTreeMap 会按姓名的字典序排列)
println!("=== 所有学生分数(按姓名排序) ===");
for (name, score) in &scores {
println!("{}: {}分", name, score);
}
// 2. 获取并输出所有90分及以上的优秀学生
let excellent: Vec<_> = scores.iter()
.filter(|(_, &score)| score >= 90) // 筛选分数≥90的条目
.collect();
println!("\n=== 90分及以上的优秀学生 ===");
for (name, score) in excellent {
println!("{}: {}分", name, score);
}
// 3. 范围查询:获取姓名首字母在 "B" 到 "D" 之间的学生(字典序范围)
let range: Vec<_> = scores.range("B"..="D").collect();
println!("\n=== 姓名在 B-D 范围内的学生 ===");
for (name, score) in range {
println!("{}: {}分", name, score);
}
// 4. 获取并输出排序后的第一个和最后一个元素(按姓名字典序)
println!("\n=== 按姓名排序的首尾元素 ===");
if let Some((name, score)) = scores.first_key_value() {
println!("第一个(字典序最小): {} - {}分", name, score);
}
if let Some((name, score)) = scores.last_key_value() {
println!("最后一个(字典序最大): {} - {}分", name, score);
}
}

2. VecDeque:双端队列
VecDeque<T> 支持在两端高效插入和删除,是实现队列和栈的理想选择。
2.1 基本操作
rust
use std::collections::VecDeque;
let mut deque = VecDeque::new();
// 后端操作(类似 Vec)
deque.push_back(1);
deque.push_back(2);
deque.push_back(3);
// 前端操作(Vec 不支持)
deque.push_front(0);
println!("{:?}", deque); // [0, 1, 2, 3]
// 弹出操作
let front = deque.pop_front(); // Some(0)
let back = deque.pop_back(); // Some(3)
// 访问
let first = deque.front(); // Some(&1)
let last = deque.back(); // Some(&2)
2.2 性能特点
- 两端插入/删除:O(1) 摊销时间
- 中间插入/删除:O(n)
- 随机访问:O(1)
2.3 应用场景
rust
// 队列(FIFO):使用 push_back + pop_front
fn queue_demo() {
let mut queue = VecDeque::new();
queue.push_back("task1");
queue.push_back("task2");
while let Some(task) = queue.pop_front() {
println!("处理: {}", task);
}
}
// 栈(LIFO):使用 push_back + pop_back(或 push_front + pop_front)
fn stack_demo() {
let mut stack = VecDeque::new();
stack.push_back("item1");
stack.push_back("item2");
while let Some(item) = stack.pop_back() {
println!("弹出: {}", item);
}
}
// 滑动窗口:双端队列的经典应用
fn sliding_window(nums: &[i32], k: usize) -> Vec<i32> {
let mut deque = VecDeque::new();
let mut result = Vec::new();
for (i, &num) in nums.iter().enumerate() {
// 移除窗口外的元素
if let Some(&idx) = deque.front() {
if idx + k <= i {
deque.pop_front();
}
}
// 维护递减队列(用于找最大值)
while let Some(&idx) = deque.back() {
if nums[idx] < num {
deque.pop_back();
} else {
break;
}
}
deque.push_back(i);
// 窗口形成后记录最大值
if i >= k - 1 {
result.push(nums[deque[0]]);
}
}
result
}
3. BinaryHeap:优先队列/最大堆
BinaryHeap<T> 实现最大堆(Max Heap),根节点始终是最大值。
3.1 基本操作
rust
use std::collections::BinaryHeap;
let mut heap = BinaryHeap::new();
// 插入元素
heap.push(3);
heap.push(1);
heap.push(4);
heap.push(1);
heap.push(5);
// 获取最大值(不移除)
println!("最大值: {:?}", heap.peek()); // Some(5)
// 弹出最大值
while let Some(max) = heap.pop() {
println!("{}", max);
}
// 输出:5, 4, 3, 1, 1(从大到小)
3.2 最小堆实现
BinaryHeap 默认是最大堆,要实现最小堆,需要:
rust
use std::collections::BinaryHeap;
use std::cmp::Reverse;
// 方法1:使用 Reverse 包装
let mut min_heap = BinaryHeap::new();
min_heap.push(Reverse(5));
min_heap.push(Reverse(1));
min_heap.push(Reverse(3));
if let Some(Reverse(min)) = min_heap.pop() {
println!("最小值: {}", min); // 1
}
// 方法2:自定义结构体实现 Ord
#[derive(Debug, Eq, PartialEq)]
struct MinItem(i32);
impl Ord for MinItem {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
other.0.cmp(&self.0) // 反向比较
}
}
impl PartialOrd for MinItem {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
let mut custom_heap = BinaryHeap::new();
custom_heap.push(MinItem(5));
custom_heap.push(MinItem(1));
if let Some(MinItem(min)) = custom_heap.pop() {
println!("最小值: {}", min); // 1
}
3.3 应用场景
rust
// 1. Top K 问题
fn top_k_largest(nums: Vec<i32>, k: usize) -> Vec<i32> {
let mut heap = BinaryHeap::new();
for num in nums {
heap.push(num);
if heap.len() > k {
heap.pop();
}
}
heap.into_sorted_vec().into_iter().rev().collect()
}
// 2. 合并 K 个有序数组
fn merge_k_sorted(arrays: Vec<Vec<i32>>) -> Vec<i32> {
let mut heap = BinaryHeap::new();
let mut result = Vec::new();
// 初始化:每个数组的第一个元素
for (arr_idx, arr) in arrays.iter().enumerate() {
if !arr.is_empty() {
heap.push((Reverse(arr[0]), arr_idx, 0));
}
}
while let Some((Reverse(val), arr_idx, elem_idx)) = heap.pop() {
result.push(val);
let arr = &arrays[arr_idx];
if elem_idx + 1 < arr.len() {
heap.push((Reverse(arr[elem_idx + 1]), arr_idx, elem_idx + 1));
}
}
result
}
// 3. 任务调度(按优先级)
#[derive(Debug, PartialEq, Eq)]
struct Task {
priority: i32,
name: String,
}
impl Ord for Task {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
other.priority.cmp(&self.priority) // 高优先级先执行
}
}
impl PartialOrd for Task {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
fn task_scheduler() {
let mut queue = BinaryHeap::new();
queue.push(Task { priority: 3, name: "低优先级任务".into() });
queue.push(Task { priority: 10, name: "高优先级任务".into() });
queue.push(Task { priority: 5, name: "中优先级任务".into() });
while let Some(task) = queue.pop() {
println!("执行: {} (优先级: {})", task.name, task.priority);
}
}
4. LinkedList:链表(不推荐常用)
LinkedList<T> 是双向链表,但在 Rust 中不常用,因为:
- 缓存不友好:节点分散在内存中
- 性能较差:即使是简单操作也可能较慢
- Vec 更优 :大多数场景
Vec或VecDeque性能更好
rust
use std::collections::LinkedList;
let mut list = LinkedList::new();
list.push_back(1);
list.push_back(2);
list.push_front(0);
// 仅在需要频繁中间插入/删除时考虑使用
// 通常 VecDeque 是更好的选择
5. 集合类型选择指南
5.1 映射类型对比
| 需求 | 推荐类型 | 理由 |
|---|---|---|
| 快速查找,无序 | HashMap |
O(1) 平均时间复杂度 |
| 需要按键排序 | BTreeMap |
有序,支持范围查询 |
| 小数据集(< 100项) | 任意 | 性能差异可忽略 |
| 需要范围查询 | BTreeMap |
HashMap 不支持 |
5.2 集合类型对比
| 需求 | 推荐类型 | 理由 |
|---|---|---|
| 快速查找,无序 | HashSet |
O(1) 平均 |
| 需要排序,范围查询 | BTreeSet |
有序集合 |
| 需要找最值 | BTreeSet |
first/last 方法 |
| 简单去重 | HashSet |
性能最优 |
5.3 队列/栈选择
| 需求 | 推荐类型 | 理由 |
|---|---|---|
| 只需要后端操作 | Vec |
最简单 |
| 需要双端操作 | VecDeque |
两端 O(1) |
| 需要优先级 | BinaryHeap |
优先队列 |
| 需要中间插入 | VecDeque 或 LinkedList |
根据频率选择 |
6. 性能基准对比
rust
use std::collections::{HashMap, BTreeMap};
use std::time::Instant;
fn benchmark_lookup() {
let size = 100_000;
// HashMap
let mut hash_map = HashMap::new();
for i in 0..size {
hash_map.insert(i, i * 2);
}
let start = Instant::now();
for i in 0..size {
let _ = hash_map.get(&i);
}
println!("HashMap 查找: {:?}", start.elapsed());
// BTreeMap
let mut btree_map = BTreeMap::new();
for i in 0..size {
btree_map.insert(i, i * 2);
}
let start = Instant::now();
for i in 0..size {
let _ = btree_map.get(&i);
}
println!("BTreeMap 查找: {:?}", start.elapsed());
// 通常 HashMap 快 2-5 倍
}
7. 常见错误与修复
错误1:误用 HashMap 需要排序的场景
rust
// ❌ 错误:需要排序但用了 HashMap
let mut map = HashMap::new();
map.insert("z", 1);
map.insert("a", 2);
for (k, v) in &map {
println!("{}: {}", k, v); // 顺序不确定
}
// ✅ 修复:使用 BTreeMap
let mut map = BTreeMap::new();
map.insert("z", 1);
map.insert("a", 2);
for (k, v) in &map {
println!("{}: {}", k, v); // a: 2, z: 1(有序)
}
错误2:BinaryHeap 想实现最小堆但忘记 Reverse
rust
// ❌ 错误:默认是最大堆
let mut heap = BinaryHeap::new();
heap.push(5);
heap.push(1);
println!("{}", heap.peek().unwrap()); // 5(最大值)
// ✅ 修复:使用 Reverse
let mut heap = BinaryHeap::new();
heap.push(Reverse(5));
heap.push(Reverse(1));
println!("{}", heap.peek().unwrap().0); // 1(最小值)
8. 实战练习
- 实现 LRU 缓存 :使用
VecDeque或LinkedList实现最近最少使用缓存。 - 优先级任务队列 :使用
BinaryHeap实现任务调度器,高优先级任务先执行。 - 有序分数排名 :使用
BTreeMap存储学生成绩,支持按分数范围查询。 - 滑动窗口最大值 :使用
VecDeque高效实现滑动窗口最大值问题。
9. 总结
选择原则
- HashMap vs BTreeMap :需要排序或范围查询用
BTreeMap,否则用HashMap - Vec vs VecDeque :只需要后端操作用
Vec,需要双端操作用VecDeque - BinaryHeap:适合优先队列、Top K、任务调度
- LinkedList :很少使用,优先考虑
Vec或VecDeque
性能要点
HashMap查找最快(O(1) 平均)BTreeMap支持有序和范围查询(O(log n))VecDeque双端操作高效(O(1) 摊销)BinaryHeap适合优先级场景(O(log n) 插入/删除)
下一节 将进入结构体与枚举的学习,这是 Rust 自定义类型的核心。