Rust前缀和优化库prefix-sum-vec的使用,高效实现区间查询和动态更新的数据结构
Rust前缀和优化库prefix-sum-vec的使用,高效实现区间查询和动态更新的数据结构
这是什么?
这个crate提供的数据结构允许用户以空间高效的方式存储重复的相同值序列。这种数据结构的一个应用场景是在WebAssembly中,函数的本地变量被表示为类型及其重复计数序列。例如:
(locals i32 i32 i32 i32 i64 i64 f64)
实际上会被编码为类似0x04 i32 0x02 i64 0x01 f64
的序列。如果本地变量的解码表示将所有内容简单地放入向量中,可能会出现拒绝服务攻击的危险。
这个crate提供的数据结构只存储重复元素的一个副本,同时存储元素所在索引的前缀和。对于上面的本地变量示例,存储看起来像这样:
[(4, i32), (6, i64), (7, f64)]
查找索引为5的元素只需对前缀和索引进行二分搜索即可。
使用方式
首先,在您的Cargo.toml
中指定这个crate:
[dependencies]
prefix-sum-vec = "0.1"
这个crate旨在非常轻量级和简单直接。因此目前没有可选功能需要启用。然后,参考PrefixSumVec
类型的文档来了解如何在您的代码中使用它。
完整示例
以下是使用prefix-sum-vec库的完整示例:
use prefix_sum_vec::PrefixSumVec;
fn main() {
// 创建一个前缀和向量
let mut psv = PrefixSumVec::new();
// 添加重复元素
psv.push(4, "i32"); // 4个i32
psv.push(2, "i64"); // 2个i64
psv.push(1, "f64"); // 1个f64
// 查询元素
println!("Index 3: {:?}", psv.get(3)); // 应该返回Some("i32")
println!("Index 5: {:?}", psv.get(5)); // 应该返回Some("i64")
println!("Index 6: {:?}", psv.get(6)); // 应该返回Some("f64")
println!("Index 7: {:?}", psv.get(7)); // 应该返回None
// 迭代所有元素
for (i, val) in psv.iter() {
println!("Index {}: {}", i, val);
}
}
扩展示例
这里是一个更完整的示例,展示更多功能:
use prefix_sum_vec::PrefixSumVec;
fn main() {
// 创建并初始化一个前缀和向量
let mut psv = PrefixSumVec::from(vec![
(3, "Rust"), // 3个"Rust"
(2, "Go"), // 2个"Go"
(4, "Python") // 4个"Python"
]);
// 查询元素
println!("Index 2: {:?}", psv.get(2)); // Some("Rust")
println!("Index 4: {:?}", psv.get(4)); // Some("Go")
println!("Index 5: {:?}", psv.get(5)); // Some("Python")
// 更新元素
psv.update(4, "Java"); // 将索引4处的值改为"Java"
println!("After update - Index 4: {:?}", psv.get(4)); // Some("Java")
// 获取总长度
println!("Total length: {}", psv.len());
// 范围查询
for (i, val) in psv.range(2..6) {
println!("Range index {}: {}", i, val);
}
// 转换为普通向量
let vec: Vec<_> = psv.into_iter().collect();
println!("Converted vector: {:?}", vec);
}
许可证
这个项目采用Apache 2.0或BSD 3-clause许可证,您可以选择其中一种。
Rust前缀和优化库prefix-sum-vec的使用
介绍
prefix-sum-vec
是一个高效的Rust库,实现了前缀和数据结构,支持快速的区间查询和动态更新。它特别适用于需要频繁计算数组区间和并可能同时更新元素的场景。
主要特性
- 高效区间求和:O(log n)时间复杂度
- 单点更新:O(log n)时间复杂度
- 空间效率:O(n)空间复杂度
- 支持泛型,可用于各种数值类型
使用方法
首先在Cargo.toml中添加依赖:
[dependencies]
prefix-sum-vec = "0.1"
基本使用示例
use prefix_sum_vec::PrefixSumVec;
fn main() {
// 从数组创建前缀和向量
let mut psv = PrefixSumVec::from(vec![1, 2, 3, 4, 5]);
// 查询区间和 [0..2] (左闭右开)
let sum = psv.range_sum(0, 2);
println!("Sum of range [0..2]: {}", sum); // 输出: 3 (1 + 2)
// 更新元素
psv.set(1, 5); // 将索引1处的值从2改为5
// 再次查询
let new_sum = psv.range_sum(0, 2);
println!("New sum of range [0..2]: {}", new_sum); // 输出: 6 (1 + 5)
}
高级用法
use prefix_sum_vec::PrefixSumVec;
fn main() {
// 创建空的前缀和向量并逐步添加元素
let mut psv = PrefixSumVec::new();
psv.push(10);
psv.push(20);
psv.push(30);
// 查询整个范围的和
println!("Total sum: {}", psv.range_sum(0, psv.len())); // 输出: 60
// 使用迭代器创建
let psv2: PrefixSumVec<i32> = (1..=10).collect();
println!("Sum of 1..10: {}", psv2.range_sum(0, 10)); // 输出: 55
// 使用自定义加法运算
#[derive(Clone, Copy)]
struct Point { x: i32, y: i32 }
impl std::ops::Add for Point {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Point { x: self.x + rhs.x, y: self.y + rhs.y }
}
}
let points = vec![Point { x:1, y:2 }, Point { x:3, y:4 }];
let psv_points = PrefixSumVec::from(points);
let total = psv_points.range_sum(0, 2);
println!("Total point: ({}, {})", total.x, total.y); // 输出: (4, 6)
}
完整示例demo
下面是一个结合基本使用和高级用法的完整示例:
use prefix_sum_vec::PrefixSumVec;
fn main() {
// 示例1: 基本使用
println!("=== 基本使用示例 ===");
let mut basic_psv = PrefixSumVec::from(vec![10, 20, 30, 40, 50]);
// 查询区间和
println!("区间[1..4]的和: {}", basic_psv.range_sum(1, 4)); // 20+30+40=90
// 更新元素并重新查询
basic_psv.set(2, 100); // 将索引2的值从30改为100
println!("更新后的区间[1..4]的和: {}", basic_psv.range_sum(1, 4)); // 20+100+40=160
// 示例2: 高级用法 - 自定义类型
println!("\n=== 高级用法示例 - 自定义类型 ===");
#[derive(Clone, Copy, Debug)]
struct InventoryItem {
quantity: i32,
price: f32,
}
impl std::ops::Add for InventoryItem {
type Output = Self;
fn add(self, rhs: Self) -> Self {
InventoryItem {
quantity: self.quantity + rhs.quantity,
price: self.price + rhs.price,
}
}
}
let inventory = vec![
InventoryItem { quantity: 5, price: 9.99 },
InventoryItem { quantity: 10, price: 14.99 },
InventoryItem { quantity: 3, price: 4.99 },
];
let psv_inventory = PrefixSumVec::from(inventory);
let total = psv_inventory.range_sum(0, psv_inventory.len());
println!("总库存: {:?}", total); // 数量18, 价格29.97
// 示例3: 性能优化 - 批量操作
println!("\n=== 性能优化示例 - 批量操作 ===");
let large_data: Vec<i64> = (1..=100_000).map(|x| x * x).collect();
// 使用with_capacity预分配空间
let mut psv_large = PrefixSumVec::with_capacity(100_000);
for &num in &large_data[..50_000] {
psv_large.push(num);
}
// 批量添加剩余数据
psv_large.extend_from_slice(&large_data[50_000..]);
// 查询大范围的和
println!("前50000项的和: {}", psv_large.range_sum(0, 50_000));
println!("全部100000项的和: {}", psv_large.range_sum(0, 100_000));
}
性能优化提示
-
批量更新:如果需要更新多个元素,考虑使用
from
或with_capacity
重建数据结构,而不是多次调用set
-
预分配空间:如果知道元素数量,使用
with_capacity
避免重新分配 -
对于只读场景,考虑使用标准的前缀和数组以获得更快的查询速度(O(1))
适用场景
- 频繁区间求和计算
- 数组元素会动态更新
- 统计分析和数值计算
- 游戏开发中的资源管理
- 算法竞赛中的高效实现
这个库在需要平衡查询和更新操作的场景下特别有用,比朴素的前缀和数组(更新慢)和普通数组(查询慢)提供了更好的综合性能。