Algorithm 哈希表与平衡二叉树

Algorithm 哈希表与平衡二叉树,algorithm,language-agnostic,data-structures,hash,tree,Algorithm,Language Agnostic,Data Structures,Hash,Tree,当我需要在哈希表或平衡二叉树之间进行选择以实现集合或关联数组时,我应该考虑哪些因素?如果不需要以任何顺序保存数据,哈希表通常会更好。如果必须对数据进行排序,则二叉树更好。哈希表的查找速度更快: 您需要一个生成均匀分布的密钥(否则您将错过很多,并且必须依赖于哈希以外的内容;比如线性搜索) 散列可以使用大量的空白空间。您可以保留256个条目,但目前只需要8个条目 二叉树: 确定性。我想 不需要像哈希表那样的额外空间 必须保持分类。在中间添加元素意味着移动其余部分。 如果只需要访问单个元素,则哈

当我需要在哈希表或平衡二叉树之间进行选择以实现集合或关联数组时,我应该考虑哪些因素?

如果不需要以任何顺序保存数据,哈希表通常会更好。如果必须对数据进行排序,则二叉树更好。

哈希表的查找速度更快:

  • 您需要一个生成均匀分布的密钥(否则您将错过很多,并且必须依赖于哈希以外的内容;比如线性搜索)
  • 散列可以使用大量的空白空间。您可以保留256个条目,但目前只需要8个条目
二叉树:

  • 确定性。我想
  • 不需要像哈希表那样的额外空间

  • 必须保持分类。在中间添加元素意味着移动其余部分。

如果只需要访问单个元素,则哈希表更好。如果您需要一系列的元素,那么除了二叉树就别无选择。

现代体系结构中的一个重要观点:如果哈希表的负载因子较低,它的内存读取通常比二叉树要少。由于与消耗CPU周期相比,内存访问往往成本较高,因此哈希表的速度通常更快

在下面的示例中,假设二叉树是自平衡的,如红黑树、AVL树或treap树

另一方面,如果您在决定扩展哈希表时需要重新刷新哈希表中的所有内容,那么这可能是一个代价高昂的操作,可能会发生(摊销)。二叉树没有这个限制

二叉树更容易在纯函数语言中实现

二叉树有一个自然的排序顺序和一种自然的方式来遍历所有元素的树

当哈希表中的负载因子较低时,可能会浪费大量内存空间,但使用两个指针时,二叉树往往会占用更多空间

哈希表几乎是O(1)(取决于您如何处理负载因子)与Bin树O(lgn)


树木往往是“平均表现者”。他们没有什么做得特别好,但也没有什么做得特别差。

我担心,总的来说,这个问题无法回答

问题是有许多类型的哈希表和平衡二叉树,它们的性能差异很大

因此,天真的答案是:这取决于您需要的功能。如果不需要排序,则使用哈希表,否则使用平衡二叉树

对于一个更详细的答案,让我们考虑一些备选方案。< /P> (有关一些基础知识,请参见维基百科的条目)

  • 并非所有哈希表都使用链表作为存储桶。一种流行的替代方法是使用“更好的”存储桶,例如二叉树或另一个哈希表(带有另一个哈希函数)
  • 有些哈希表根本不使用存储桶:请参阅开放寻址(显然,它们还附带其他问题)
  • 有一种称为线性重新散列(这是实现细节的一种质量)的方法,可以避免“停止世界并重新散列”的陷阱。基本上,在迁移阶段,您只需在“new”表中插入,还可以将一个“old”条目移动到“new”表中。当然,迁移阶段意味着双重查找等
二叉树

    重平衡是昂贵的,您可以考虑跳过列表(也适用于多线程访问)或SpRead树。
  • 一个好的分配器可以将节点“打包”到内存中(更好的缓存行为),即使这不会缓解指针查找问题
  • B-Tree和变体也提供“包装”
我们不要忘记O(1)是一个渐近复杂性。对于少数元素,系数通常更重要(性能方面)。如果你的散列函数很慢,这一点尤其正确


最后,对于集合,您也可能希望考虑概率数据结构,例如

< 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