我该如何在哈希表和字典树(前缀树)之间进行选择?

22 浏览
0 Comments

我该如何在哈希表和字典树(前缀树)之间进行选择?

如果我必须在哈希表和前缀树之间做出选择,那么什么是决定性因素,会让我选择其中之一?从我自己的幼稚观点来看,使用前缀树似乎会有一些额外的开销,因为它不是以数组的形式存储,但就运行时间而言(假设最长的键是最长的英文单词),它可以基本上是O(1)(相对于上界而言)。也许最长的英文单词有50个字符?\n哈希表一旦获得索引就可以立即查找。然而,通过哈希键来获得索引似乎可能需要近50个步骤。\n有人可以给我一个更有经验的观点吗?谢谢!

0
0 Comments

在许多工业场景中,需要考虑数据结构以优化内存占用,减少缓存未命中。哈希表是一种常用的数据结构,但它的查找时间并非恒定,而是取决于哈希表的大小以及哈希函数的计算复杂度。在需要高效查找的情况下,创建大型哈希表并不是一个优雅的解决方案,特别是在高频交易等场景中,即使很小的延迟/可伸缩性也很重要。

一个非常好的例子是消息中间件,例如有一百万个订阅者和发布者发布到不同类别的消息(在JMS术语中是主题或交换机)。在这种情况下,如果你想根据主题(实际上是字符串)过滤消息,绝对不希望为一百万个订阅和数百万个主题创建哈希表。更好的方法是将主题存储在前缀树中,这样当基于主题匹配进行过滤时,其复杂度与主题/订阅者/发布者的数量无关(仅取决于字符串的长度)。我喜欢这种方法,因为你可以在这个数据结构上进行创新,优化空间需求,从而减少缓存未命中。

选择哈希表还是前缀树(Trie)取决于具体的需求。如果需要高效的查找和低延迟,同时内存占用和缓存未命中对性能有较大影响,那么前缀树是一个更好的选择。它适用于需要根据字符串进行过滤、匹配和查询的场景,尤其是当主题/字符串的数量庞大时。在设计和实现时,可以根据具体情况优化空间需求,减少缓存未命中的可能性。

0
0 Comments

哈希表和前缀树(Trie)是两种常见的数据结构,用于解决不同类型的问题。选择哪种数据结构取决于你尝试解决的问题。

如果你只需要进行插入和查找操作,那么哈希表是更好的选择。哈希表的查询操作复杂度为O(1),非常高效。但是,如果你需要解决一些更复杂的问题,例如与前缀相关的查询,那么前缀树可能是更好的解决方案。

有人可能会问,如果哈希表和前缀树在查询操作上具有相同的复杂度(对于长度为k的字符串,复杂度为O(k)),为什么我们应该选择哈希表?我认为,哈希表对字符串进行了计算,而前缀树对字符串进行了地址查找。地址查找可能会错过缓存,而计算速度更快,因为它们不会访问缓存。这是我的理解哈哈。

总之,选择哈希表还是前缀树取决于你的具体需求。如果需要进行复杂的查询操作,尤其是与前缀相关的查询,那么前缀树可能更适合。但如果只需要进行简单的插入和查找操作,那么哈希表是更高效的选择。

以下是使用Python实现哈希表和前缀树的示例代码:

# 哈希表实现
hash_table = {}
# 插入操作
hash_table['apple'] = 1
hash_table['banana'] = 2
# 查找操作
print(hash_table['apple'])  # 输出: 1
# 前缀树实现
class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_end_of_word = False
class Trie:
    def __init__(self):
        self.root = TrieNode()
    # 插入操作
    def insert(self, word):
        node = self.root
        for char in word:
            if char not in node.children:
                node.children[char] = TrieNode()
            node = node.children[char]
        node.is_end_of_word = True
    # 查找操作
    def search(self, word):
        node = self.root
        for char in word:
            if char not in node.children:
                return False
            node = node.children[char]
        return node.is_end_of_word
# 示例用法
trie = Trie()
# 插入操作
trie.insert('apple')
trie.insert('banana')
# 查找操作
print(trie.search('apple'))  # 输出: True

0
0 Comments

如何在哈希表和前缀树(Trie)之间做出选择?

前缀树的优点:

- O(k)的查找时间,其中k是键的大小

- 如果不存在,查找时间可能小于k

- 支持有序遍历

- 不需要哈希函数

- 删除操作简单

- 可以快速查找键的前缀,枚举具有给定前缀的所有条目等

链接结构的优点:

- 如果有许多公共前缀,它们所需的空间是共享的

- 不可变的前缀树可以共享结构。不需要直接更新前缀树,可以构建一个新的前缀树,只在一条分支上有所不同,而在其他地方指向旧的前缀树。这对于并发性、多个同时版本的表格等非常有用

- 不可变的前缀树是可压缩的。也就是说,它可以通过哈希共享后缀的结构

哈希表的优点:

- 大家都了解哈希表,对吧?你的系统已经有一个很好的、经过优化的实现,对于大多数目的来说比前缀树更快

- 键不需要任何特殊结构

- 比明显的链接前缀树结构更节省空间

关于“比明显的链接前缀树结构更节省空间”我不太同意——在一般的哈希表实现中,它占用的空间要比键的空间大得多,而在前缀树中,每个节点表示一个单词。从这个意义上说,前缀树更节省空间。

访问哪种结构的数据?我在考虑缓存和位置的因素,这与我的经验相矛盾:例如,在我测量的所有结构中,前缀树的空间利用率最差。这是有道理的,因为指针比一个字节要大得多。是的,共享前缀有助于减少空间,但它必须克服很多开销才能达到相同的水平。更节省空间的表示方法可以大有裨益,但这时我们不再谈论明显的链接结构。

处理电话号码计划似乎是前缀树的一个合理场景。例如,电话号码到运营商的匹配,包括从一个运营商转移到另一个运营商的号码。对于通常的字典,这可能取决于语言(普通话与英语),你需要n-gram和/或其他统计数据。对于韵书,后缀树也是一个很好的选择。

数据的多样性对查找很重要。如果你的数据值中有很大比例是唯一的,由于使用了额外的空指针,空间复杂度会增加。

前缀树是否必须支持有序遍历?在我看来,如果没有一些支持结构,要在不扫描稀疏的子节点数组的情况下遍历它们是不可能的。

对于删除操作,需要递归地向上删除父节点。

0