当前位置: 首页 > news >正文

Rust专项——其他集合类型详解:BTreeMap、VecDeque、BinaryHeap

在学习了 VecHashMapHashSet 之后,本节介绍 Rust 标准库中的其他重要集合类型:BTreeMap/BTreeSet(有序映射/集合)、VecDeque(双端队列)、BinaryHeap(优先队列/堆),以及它们的适用场景与性能特点。


1. BTreeMap 与 BTreeSet:有序数据结构

1.1 BTreeMap:基于 B 树的有序映射

BTreeMap<K, V> 提供按键排序的键值对存储,所有键都是有序的。

use std::collections::BTreeMap;fn main() {// 创建一个空的 BTreeMaplet 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 的对比

特性HashMapBTreeMap
顺序无序有序(按键排序)
查找O(1) 平均O(log n)
插入O(1) 平均O(log n)
范围查询❌ 不支持✅ 支持
内存开销较大(哈希表)较小(B树)
适用场景快速查找、无序访问需要排序、范围查询

1.2 BTreeSet:有序集合

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 范围查询与迭代器

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 基本操作

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 应用场景

// 队列(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 基本操作

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 默认是最大堆,要实现最小堆,需要:

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 应用场景

// 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 更优:大多数场景 VecVecDeque 性能更好
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 映射类型对比

需求推荐类型理由
快速查找,无序HashMapO(1) 平均时间复杂度
需要按键排序BTreeMap有序,支持范围查询
小数据集(< 100项)任意性能差异可忽略
需要范围查询BTreeMapHashMap 不支持

5.2 集合类型对比

需求推荐类型理由
快速查找,无序HashSetO(1) 平均
需要排序,范围查询BTreeSet有序集合
需要找最值BTreeSetfirst/last 方法
简单去重HashSet性能最优

5.3 队列/栈选择

需求推荐类型理由
只需要后端操作Vec最简单
需要双端操作VecDeque两端 O(1)
需要优先级BinaryHeap优先队列
需要中间插入VecDequeLinkedList根据频率选择

6. 性能基准对比

use std::collections::{HashMap, BTreeMap};
use std::time::Instant;fn benchmark_lookup() {let size = 100_000;// HashMaplet 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());// BTreeMaplet 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 需要排序的场景

// ❌ 错误:需要排序但用了 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

// ❌ 错误:默认是最大堆
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. 实战练习

  1. 实现 LRU 缓存:使用 VecDequeLinkedList 实现最近最少使用缓存。
  2. 优先级任务队列:使用 BinaryHeap 实现任务调度器,高优先级任务先执行。
  3. 有序分数排名:使用 BTreeMap 存储学生成绩,支持按分数范围查询。
  4. 滑动窗口最大值:使用 VecDeque 高效实现滑动窗口最大值问题。

9. 总结

选择原则

  • HashMap vs BTreeMap:需要排序或范围查询用 BTreeMap,否则用 HashMap
  • Vec vs VecDeque:只需要后端操作用 Vec,需要双端操作用 VecDeque
  • BinaryHeap:适合优先队列、Top K、任务调度
  • LinkedList:很少使用,优先考虑 VecVecDeque

性能要点

  • HashMap 查找最快(O(1) 平均)
  • BTreeMap 支持有序和范围查询(O(log n))
  • VecDeque 双端操作高效(O(1) 摊销)
  • BinaryHeap 适合优先级场景(O(log n) 插入/删除)

下一节 将进入结构体与枚举的学习,这是 Rust 自定义类型的核心。

http://www.dtcms.com/a/552755.html

相关文章:

  • 软件开发模式架构选择
  • 网站开发设计注册注册小程序
  • Git命令(三)
  • Spring Security 新手学习教程
  • 72.是否可以把所有Bean都通过Spring容器来管
  • DevExpress WPF中文教程:Data Grid - 如何使用虚拟源?(四)
  • 车载软件需求开发与管理 --- 需求收集与整理
  • [linux仓库]线程控制[线程·叁]
  • 从工行“余额归零”事件看CAP定理:当金融系统在一致性与可用性之间做出选择
  • Java的stream使用方案
  • 给网站做视频怎么赚钱电影网站系统源码
  • React Server Components 进阶:数据预取与缓存
  • MR30分布式I/O助力物流分拣系统智能化升级
  • 当UAF漏洞敲响提权警钟:技术剖析与应对之道
  • Flink(用Scala版本写Word Count 出现假报错情况解决方案)假报错,一直显示红色报错
  • Smartbi 10 月版本亮点:AIChat对话能力提升,国产化部署更安全
  • 网站备案单位商业网站源码免费下载
  • 外贸网站经典营销案例搭建服务器做网站
  • MQTT 协议详解与工业物联网架构设计指南
  • JMeter WebSocket异步接口测试简明指南
  • [论文]Colmap-PCD: An Open-source Tool for Fine Image-to-point cloud Registration
  • 网站开发合作协议自主建站系统
  • MySQL 8 查询逗号分隔字符串
  • react 源码2
  • 淮南电商网站建设苏州网站优化
  • AI应用市场崛起:聊天机器人、教育学习、视频创作三驾马车驱动创新
  • SQL 学习笔记
  • 医药网站建设中图片app开发公司 弙东
  • ProfiNet转ModbusTCP实战:工业智能网关让S7-1516与上位机3ms握手
  • 巨 椰 云手机和云真机哪个个比较好用?