Algorithm 哈希表与平衡二叉树
当我需要在哈希表或平衡二叉树之间进行选择以实现集合或关联数组时,我应该考虑哪些因素?如果不需要以任何顺序保存数据,哈希表通常会更好。如果必须对数据进行排序,则二叉树更好。哈希表的查找速度更快:Algorithm 哈希表与平衡二叉树,algorithm,language-agnostic,data-structures,hash,tree,Algorithm,Language Agnostic,Data Structures,Hash,Tree,当我需要在哈希表或平衡二叉树之间进行选择以实现集合或关联数组时,我应该考虑哪些因素?如果不需要以任何顺序保存数据,哈希表通常会更好。如果必须对数据进行排序,则二叉树更好。哈希表的查找速度更快: 您需要一个生成均匀分布的密钥(否则您将错过很多,并且必须依赖于哈希以外的内容;比如线性搜索) 散列可以使用大量的空白空间。您可以保留256个条目,但目前只需要8个条目 二叉树: 确定性。我想 不需要像哈希表那样的额外空间 必须保持分类。在中间添加元素意味着移动其余部分。 如果只需要访问单个元素,则哈
- 您需要一个生成均匀分布的密钥(否则您将错过很多,并且必须依赖于哈希以外的内容;比如线性搜索)
- 散列可以使用大量的空白空间。您可以保留256个条目,但目前只需要8个条目
- 确定性。我想
- 不需要像哈希表那样的额外空间
- 必须保持分类。在中间添加元素意味着移动其余部分。
树木往往是“平均表现者”。他们没有什么做得特别好,但也没有什么做得特别差。我担心,总的来说,这个问题无法回答 问题是有许多类型的哈希表和平衡二叉树,它们的性能差异很大 因此,天真的答案是:这取决于您需要的功能。如果不需要排序,则使用哈希表,否则使用平衡二叉树 对于一个更详细的答案,让我们考虑一些备选方案。< /P> (有关一些基础知识,请参见维基百科的条目)
- 并非所有哈希表都使用链表作为存储桶。一种流行的替代方法是使用“更好的”存储桶,例如二叉树或另一个哈希表(带有另一个哈希函数)
- 有些哈希表根本不使用存储桶:请参阅开放寻址(显然,它们还附带其他问题)
- 有一种称为线性重新散列(这是实现细节的一种质量)的方法,可以避免“停止世界并重新散列”的陷阱。基本上,在迁移阶段,您只需在“new”表中插入,还可以将一个“old”条目移动到“new”表中。当然,迁移阶段意味着双重查找等
- 一个好的分配器可以将节点“打包”到内存中(更好的缓存行为),即使这不会缓解指针查找问题
- B-Tree和变体也提供“包装”
最后,对于集合,您也可能希望考虑概率数据结构,例如
< P> >在上面的其他伟大答案中,我会说:如果数据量不变(例如存储常量),则使用哈希表;但是,如果数据量将发生变化,请使用树。这是因为,在哈希表中,一旦达到加载因子,哈希表就必须调整大小。调整大小的操作可能非常慢。二元搜索树需要键之间的总顺序关系。哈希表只需要具有一致哈希函数的等价关系或标识关系 如果总顺序关系可用,则排序数组的查找性能可与二叉树相媲美,最坏情况下的插入性能按哈希表的顺序排列,并且复杂性和内存使用比二者都少 如果可以接受将最坏情况查找复杂度增加到O(K)或O(logk)(如果元素可以排序),则哈希表的最坏情况插入复杂度可以保持在O(1)/O(logk)(K是具有相同哈希的元素数) 如果密钥发生更改,则恢复树和哈希表的不变量的代价很高,但对于排序数组,恢复不变量的代价小于O(n logn) 在决定使用哪种实现时,需要考虑以下因素:
如果您有许多稍微不同的集合实例,您可能希望它们共享结构。这对于树很容易(如果它们是不可变的或写时复制的)。我不确定你能用哈希表做得多好;至少不那么明显。根据我的经验,hastable总是更快,因为树受到了太多的缓存影响 要查看一些真实数据,您可以查看我的TommyDS库的基准页面 这里
def bar(table):
# some intern stuck this line of code in
table["hello"] = "world"
return table["the answer"]
def foo(x, y, table):
z = bar(table)
if "hello" in table:
raise Exception("failed catastrophically!")
return x + y + z
important_result = foo(1, 2, {
"the answer": 5,
"this table": "doesn't contain hello",
"so it should": "be ok"
})
# catastrophic failure occurs