因此,如果我必须在哈希表或前缀树之间进行选择,那么有哪些区别因素会导致我选择其中一个而不是另一个。从我自己幼稚的观点来看,似乎使用 trie 有一些额外的开销,因为它不是以数组的形式存储的,但就运行时而言(假设最长的键是最长的英语单词) ,它可以基本上是 O (1)(相对于上界)。也许最长的英语单词是50个字符?
散列表是即时查找 一旦你拿到索引。然而,对获得指数的关键进行散列似乎可以轻松地走近50步。
谁能给我提供一个更有经验的观点? 谢谢!
这完全取决于你想要解决什么问题。如果您需要做的只是插入和查找,那么使用哈希表。如果您需要解决更复杂的问题,例如与前缀相关的查询,那么尝试可能是更好的解决方案。
一些(通常是嵌入式的,实时的)应用程序要求处理时间独立于数据。在这种情况下,哈希表可以保证已知的执行时间,而尝试根据数据的不同而不同。
尝试的好处:
基本知识:
新业务:
链接结构的优点:
散列表的优点:
使用树木:
大家都知道哈希表及其用法,但它并不完全是常量查找时间,它取决于哈希表有多大,哈希函数的计算复杂度。
在大多数工业场景中,即使是很小的延迟/可伸缩性问题(例如: 高频交易) ,为高效查找创建巨大的哈希表也不是一个优雅的解决方案。为了减少缓存丢失,您还必须考虑优化数据结构以适应它在内存中占用的空间。
一个非常好的例子是消息传递中间件。你有一百万个订阅者和发布者,他们把消息分成不同的类别(用 JMS 术语——主题或交换) ,在这种情况下,如果你想过滤掉基于主题(实际上是字符串)的消息,你肯定不想为这一百万个订阅和百万个主题创建哈希表。更好的方法是尝试存储主题,因此当基于主题匹配进行过滤时,其复杂性与主题/订阅/发布者的数量无关(仅取决于字符串的长度)。我喜欢它,因为您可以创造性地使用这种数据结构来优化空间需求,从而降低缓存丢失率。
有些事情我没有看到任何人明确提到,我认为是重要的记住。哈希表和各种尝试通常都有 O(k)操作,其中 k是字符串的位(或等效于字符)长度。
O(k)
k
这是假设您有一个很好的散列函数。如果你不想让“农场”和“农场动物”散列到相同的值,那么散列函数将不得不使用键的所有位,所以散列“农场动物”应该是“农场”的两倍长(除非你在某种滚动散列场景,但也有类似的操作节省场景与尝试)。如果用香草的话,就很清楚为什么插入“农场动物”的时间会是插入“农场”时间的两倍。从长远来看,压缩尝试也是如此。
与基本的 试试实现相比,HashTable 实现的空间效率更高。但是对于字符串,在大多数实际应用中排序是必要的。但是 HashTable 完全扰乱了字母顺序。现在,如果您的应用程序正在执行基于词法顺序的操作(比如部分搜索,所有前缀给定的字符串,所有单词按排序顺序) ,那么您应该使用 Tries。对于只进行查找,应该使用 HashTable (可以说,它提供了最短的查找时间)。
除此之外,三元搜索树是一个很好的选择。它的查找时间比 HashTable 多,但是在所有其他操作中都很有效。而且,它比尝试更节省空间。
尝试的插入和查找与输入字符串 O (s)的长度是线性的。
散列将为查找和插入提供一个 O (1) ,但是首先您必须基于输入字符串(同样是 O (s))计算散列。
结论: 在两种情况下,渐近时间复杂度都是线性的。
从数据角度来看,这个尝试有一些额外的开销,但是您可以选择一个压缩的尝试,这将使您再次或多或少地与散列表绑定。
为了打破僵局,问问自己这个问题: 我需要查找完整的单词吗?还是需要返回所有匹配前缀的单词?(如在预测性文本输入系统中)。对于第一种情况,去散列。它是更简单和更清晰的代码。更容易测试和维护。对于前缀或后缀重要的更详细的用例,可以尝试一下。
如果你只是为了好玩,那么尝试一下可以很好地利用周日下午的时间。