在我的程序中,我有一个巨大的字符串列表(非重复),它们与一个单词交叉.我想判断这个词是否有效——它的有效性取决于它在列表中的存在.我正在对许多不同的单词进行判断,所以速度是我最大的担忧,同时也是存储列表的内存效率.

So my question is, what is the data structure best fit for fast lookup and memory efficiency on a collection of Strings?

我目前正在使用一个BTreeSet,因为它是有序的,我想这会让搜索速度更快.此外,由于单词可能非常相似("apple"和"app"),我假设基于树的数据 struct 通过Vec存储它们的内存效率更高.

推荐答案

如果你需要exact匹配,那么你应该使用HashSet.它应该比BTreeSet更快、更高效.

但如果您需要更复杂的搜索/匹配功能,例如按公共前缀搜索,则区分大小写和;不敏感的搜索,大量带有大公共前缀的单词,等等,那么hashmap就不适合了.

在这种情况下,你可以使用trie也被称为prefix tree.它一个字符一个字符地存储单词,从而"压缩"公共前缀,如果搜索以公共前缀开头的many个单词,这也会大大加快搜索速度.

一个非常基本的实现只是散列映射的包装器:

struct Node {
    ptr: HashMap<u8, Node>,

    // you can also use a boolean to mark the end of word, but then you would need additional code to rebuild it from the bytes 
    word: Option<String>,
}

通过递归地将下一个单词字符添加到树中,或使用性能更高的迭代方法,可以将一个单词添加到树中:

   pub fn insert(&mut self, word: String) {
        let w = word.as_bytes();

        let mut node = self;
        for ch in w.iter().copied() {
            node = node.ptr.entry(ch).or_default()
        }

        // attach the word
        node.word = Some(word);
    }

基本的搜索也非常简单:

    fn find(&self, word: String) -> Option<String> {
        let word = word.as_bytes()
        let mut node = self;

        for ch in word.iter() {
            match node.ptr.get(ch) {
                Some(link) => node = link,
                None => return None,
            }
        }

        node.word.clone()
    }

但是,如果你想利用"同时"搜索任何带有前缀的单词,你应该利用递归 struct ,如图leetcode problem所示

还有一些实施方面的考虑:

  • 使用更快的散列函数,因为我们只对单个字节进行散列,而rust中的默认散列函数非常慢
  • 找到 node 后,可以从trie中"软删除" node ,以加快后续搜索.

您可以在提供的github链接中找到此类示例

Rust相关问答推荐

为什么单元类型(空元组)实现了`Extend`trait?

Box::new()会从一个堆栈复制到另一个堆吗?

如何防止Cargo 单据和Cargo 出口发布( crate )项目

什么时候使用FuturesOrdered?

`actix-web` 使用提供的 `tokio` 运行时有何用途?

如何返回 struct 体中向量的切片

为什么我们有两种方法来包含 serde_derive?

全面的 Rust Ch.16.2 - 使用捕获和 const 表达式的 struct 模式匹配

Some(v) 和 Some(&v) 有什么区别?

Rust 中的方法调用有什么区别?

哪些特征通过 `Deref` 而哪些不通过?

切片不能被 `usize` 索引?

是否可以预测堆栈溢出?

使用方法、关联函数和自由函数在 Rust 中初始化函数指针之间的区别

使用 serde_json 进一步处理字段

在 Rust 中返回对枚举变体的引用是个好主意吗?

在空表达式语句中移动的值

当 `T` 没有实现 `Debug` 时替代 `unwrap()`

以下打印数组每个元素的 Rust 代码有什么问题?

有没有比多个 push_str() 调用更好的方法将字符串链接在一起?