Rust高效前缀树实现库patricia_tree的使用,提供快速查找和插入的基数树数据结构

Rust高效前缀树实现库patricia_tree的使用,提供快速查找和插入的基数树数据结构

patricia_tree是一个基于基数树(也称为前缀树)的内存高效数据结构库。当键集合的前缀高度冗余时,基数树通过共享公共前缀路径可以显著减少内存使用量。

示例代码

以下是内容中提供的示例代码:

use patricia_tree::PatriciaMap;

let mut map = PatriciaMap::new();
map.insert("foo", 1);
map.insert("bar", 2);
map.insert("baz", 3);
assert_eq!(map.len(), 3);

assert_eq!(map.get("foo"), Some(&1));
assert_eq!(map.get("bar"), Some(&2));
assert_eq!(map.get("baz"), Some(&3));

完整示例

以下是一个更完整的示例,展示了patricia_tree库的基本用法:

use patricia_tree::PatriciaMap;

fn main() {
    // 创建一个新的PatriciaMap
    let mut map = PatriciaMap::new();
    
    // 插入键值对
    map.insert("apple", 10);
    map.insert("app", 5);
    map.insert("banana", 20);
    map.insert("band", 15);
    map.insert("orange", 30);
    
    // 检查长度
    println!("Map size: {}", map.len()); // 输出: Map size: 5
    
    // 查找值
    println!("Value for 'app': {:?}", map.get("app")); // 输出: Some(5)
    println!("Value for 'apple': {:?}", map.get("apple")); // 输出: Some(10)
    println!("Value for 'pear': {:?}", map.get("pear")); // 输出: None
    
    // 检查键是否存在
    println!("Contains 'banana': {}", map.contains_key("banana")); // 输出: true
    println!("Contains 'grape': {}", map.contains_key("grape")); // 输出: false
    
    // 删除键
    map.remove("band");
    println!("After removal, contains 'band': {}", map.contains_key("band")); // 输出: false
    println!("Map size after removal: {}", map.len()); // 输出: Map size after removal: 4
    
    // 遍历所有键值对
    println!("All entries:");
    for (key, value) in &map {
        println!("  {:?}: {}", String::from_utf8_lossy(key), value);
    }
}

性能基准

根据提供的基准测试数据,patricia_tree在内存使用方面表现优异:

  • 对于Wikipedia标题数据集(1345万行):

    • HashSet: 978MB
    • BTreeSet: 1,086MB
    • PatriciaSet: 424MB
  • 对于Google 5-gram数据集(981万行):

    • HashSet: 1,089MB
    • BTreeSet: 920MB
    • PatriciaSet: 218MB

安装

在项目中添加以下依赖到Cargo.toml:

patricia_tree = "0.9.0"

或者运行命令:

cargo add patricia_tree

许可证

该项目使用MIT许可证。


1 回复

Rust高效前缀树实现库patricia_tree使用指南

简介

patricia_tree是Rust中一个高效的前缀树(基数树/Patricia trie)实现库。它提供了快速查找和插入操作,特别适合需要前缀匹配的场景,如IP路由表、自动补全、字典实现等。

主要特性

  • 基于基数树(radix tree)实现,空间效率高
  • 支持快速的键值插入和查找
  • 提供前缀搜索功能
  • 线程安全(支持多线程环境)
  • 低内存开销

安装

在Cargo.toml中添加依赖:

[dependencies]
patricia_tree = "0.3"

基本使用方法

创建和插入元素

use patricia_tree::PatriciaMap;

fn main() {
    // 创建一个新的前缀树映射
    let mut tree = PatriciaMap::new();
    
    // 插入键值对
    tree.insert("apple", 1);
    tree.insert("app", 2);
    tree.insert("banana", 3);
    tree.insert("band", 4);
    
    println!("Tree size: {}", tree.len()); // 输出: Tree size: 4
}

查找元素

use patricia_tree::PatriciaMap;

fn main() {
    let mut tree = PatriciaMap::new();
    tree.insert("apple", 1);
    tree.insert("app", 2);
    
    // 精确查找
    if let Some(value) = tree.get("apple") {
        println!("Found apple: {}", value); // 输出: Found apple: 1
    }
    
    // 检查键是否存在
    println!("Contains 'app': {}", tree.contains_key("app")); // 输出: Contains 'app': true
}

前缀搜索

use patricia_tree::PatriciaMap;

fn main() {
    let mut tree = PatriciaMap::new();
    tree.insert("apple", 1);
    tree.insert("app", 2);
    tree.insert("application", 3);
    tree.insert("banana", 4);
    
    // 查找所有以"app"为前缀的键值对
    let prefix_matches: Vec<_> = tree.iter_prefix("app").collect();
    println!("Prefix 'app' matches: {:?}", prefix_matches);
    // 输出: Prefix 'app' matches: [("app", 2), ("apple", 1), ("application", 3)]
}

删除元素

use patricia_tree::PatriciaMap;

fn main() {
    let mut tree = PatriciaMap::new();
    tree.insert("apple", 1);
    tree.insert("app", 2);
    
    // 删除键
    let removed = tree.remove("app");
    println!("Removed value: {:?}", removed); // 输出: Removed value: Some(2)
    println!("Contains 'app': {}", tree.contains_key("app")); // 输出: Contains 'app': false
}

高级用法

最长前缀匹配

use patricia_tree::PatriciaMap;

fn main() {
    let mut tree = PatriciaMap::new();
    tree.insert("192.168.1.0/24", "Network 1");
    tree.insert("192.168.0.0/16", "Network 2");
    
    // 查找最长匹配前缀
    if let Some((prefix, value)) = tree.get_longest_common_prefix("192.168.1.100") {
        println!("Longest prefix match: {} => {}", prefix, value);
        // 输出: Longest prefix match: 192.168.1.0/24 => Network 1
    }
}

遍历所有元素

use patricia_tree::PatriciaMap;

fn main() {
    let mut tree = PatriciaMap::new();
    tree.insert("apple", 1);
    tree.insert("banana", 2);
    tree.insert("cherry", 3);
    
    // 遍历所有键值对
    for (key, value) in &tree {
        println!("{}: {}", key, value);
    }
    // 输出:
    // apple: 1
    // banana: 2
    // cherry: 3
}

性能提示

  1. 对于大量数据,考虑使用PatriciaMap而不是标准库的HashMap,特别是当键有共同前缀时
  2. 前缀搜索操作非常高效,适合实现自动补全功能
  3. 对于IP路由表等场景,最长前缀匹配功能特别有用

总结

patricia_tree库提供了Rust中高效的前缀树实现,特别适合需要前缀匹配和搜索的场景。它的API设计简洁,性能优越,是处理字符串或字节序列键值对的理想选择。

完整示例

下面是一个完整的示例,展示了patricia_tree库的主要功能:

use patricia_tree::PatriciaMap;

fn main() {
    // 1. 创建和插入元素
    let mut tree = PatriciaMap::new();
    tree.insert("apple", 1);
    tree.insert("app", 2);
    tree.insert("application", 3);
    tree.insert("banana", 4);
    tree.insert("band", 5);
    
    // 2. 查找元素
    println!("=== 查找元素 ===");
    if let Some(value) = tree.get("apple") {
        println!("找到apple: {}", value);
    }
    println!("包含'app': {}", tree.contains_key("app"));
    
    // 3. 前缀搜索
    println!("\n=== 前缀搜索 ===");
    let prefix_matches: Vec<_> = tree.iter_prefix("app").collect();
    println!("'app'前缀匹配: {:?}", prefix_matches);
    
    // 4. 删除元素
    println!("\n=== 删除元素 ===");
    let removed = tree.remove("app");
    println!("删除'app': {:?}", removed);
    println!("删除后包含'app': {}", tree.contains_key("app"));
    
    // 5. 最长前缀匹配
    println!("\n=== 最长前缀匹配 ===");
    let mut ip_tree = PatriciaMap::new();
    ip_tree.insert("192.168.1.0/24", "局域网1");
    ip_tree.insert("192.168.0.0/16", "局域网2");
    
    if let Some((prefix, value)) = ip_tree.get_longest_common_prefix("192.168.1.100") {
        println!("最长前缀匹配: {} => {}", prefix, value);
    }
    
    // 6. 遍历所有元素
    println!("\n=== 遍历所有元素 ===");
    for (key, value) in &tree {
        println!("{}: {}", key, value);
    }
    
    // 7. 树的基本信息
    println!("\n=== 树信息 ===");
    println!("树大小: {}", tree.len());
    println!("是否为空: {}", tree.is_empty());
}

这个完整示例展示了:

  1. 创建前缀树和插入元素
  2. 精确查找和存在性检查
  3. 前缀搜索功能
  4. 删除元素操作
  5. 最长前缀匹配(模拟IP路由表场景)
  6. 遍历所有元素
  7. 获取树的基本信息

输出结果类似于:

=== 查找元素 ===
找到apple: 1
包含'app': true

=== 前缀搜索 ===
'app'前缀匹配: [("app", 2), ("apple", 1), ("application", 3)]

=== 删除元素 ===
删除'app': Some(2)
删除后包含'app': false

=== 最长前缀匹配 ===
最长前缀匹配: 192.168.1.0/24 => 局域网1

=== 遍历所有元素 ===
apple: 1
application: 3
banana: 4
band: 5

=== 树信息 ===
树大小: 4
是否为空: false
回到顶部