哈希表 v 自平衡搜索树
Posted
技术标签:
【中文标题】哈希表 v 自平衡搜索树【英文标题】:Hash tables v self-balancing search trees 【发布时间】:2011-03-16 23:14:24 【问题描述】:我很想知道使用自平衡树技术存储项目而不是使用哈希表的原因是什么。
我看到哈希表无法维护插入顺序,但我总是可以使用顶部的链表来存储插入顺序序列。
我发现对于少量值,哈希函数会增加成本,但我总是可以将哈希函数与键一起保存以加快查找速度。
我知道哈希表比直接实现红黑树更难实现,但在实际实现中,难道不想多花点功夫吗?
我看到对于哈希表,发生冲突是正常的,但是对于允许将键保存在哈希表本身中的双哈希等开放寻址技术,问题并没有减少到没有的效果对此类实现偏爱红黑树?
我很好奇我是否完全忽略了哈希表的一个缺点,它仍然使红黑树在实际应用程序(如文件系统等)中非常可行的数据结构。
【问题讨论】:
两种数据结构各有利弊。您应该选择更适合您的问题的那个。 【参考方案1】:我能想到的几个原因:
树是动态的(空间复杂度为 N),而哈希表通常实现为固定大小的数组,这意味着它们通常会以 K 大小进行初始化,其中 K > N,所以即使你哈希图中只有 1 个元素,您可能仍有 100 个占用内存的空槽。这样做的另一个效果是:
增加基于数组的哈希表的大小是昂贵的(O(N) 平均时间,O(N log N) 最坏情况),而树可以在恒定时间内生长 (O(1)) + (定位插入点的时间(O(log N))
可以按排序顺序收集树中的元素(使用例如:in-order-traversal)。因此,您经常会得到一个排序列表,作为树木的免费福利。 与 hashmap 相比,树在最坏情况下的性能可能更好,具体取决于 hashmap 的实现方式(例如:带有链接的 hashmap 将有 O(N) 最坏情况,而自平衡树可以保证 O(log N) 最坏情况所有操作的案例)。自平衡树和哈希图在最好的最坏情况下(假设哈希图确实处理冲突)的最坏情况效率为 O(log N),但哈希图可以有更好的平均情况下的性能(通常接近 O(1)),而树将有一个常数 O(log N)。这是因为即使你的 hashmap 可以在 O(1) 中找到插入索引,它也必须考虑 hash colissions(多个元素散列到相同的数组索引),因此在最好的情况下会降级为自平衡树(如hashmap的Java实现),即hashmap中的每个元素都可以实现为一棵自平衡树,将所有经过hash的元素存储到给定的数组单元格中。
【讨论】:
【参考方案2】:我认为如果你想查询一系列键而不是一个键,自平衡树结构将比哈希表结构执行得更好。
【讨论】:
这听起来很像一个意见。你有任何数据/参考来支持你的说法吗? 查询哈希表中的一系列键可能意味着确切地知道哪里有键,哪里没有。您需要对您认为在查询间隔中的每个可能的键重复散列操作。使用树,您只需定位区间的开头,然后按顺序遍历树,直到查询区间结束。【参考方案3】:只是想补充:
平衡二叉树具有可预测的获取数据的时间 [log n],与数据类型无关。很多时候,估计应用程序的响应时间可能对您的应用程序很重要。 [哈希表可能有不可预测的响应时间]。请记住,对于更小的 n,因为在大多数常见用例中,内存查找中的性能差异几乎不重要,并且系统的瓶颈将在其他地方,有时您只是想让系统更简单调试和分析。
与哈希表相比,树通常具有更高的内存效率,并且在无需对输入键的分布和可能的冲突等进行任何分析的情况下实现起来也更加简单。
【讨论】:
【参考方案4】:存储分配是另一个考虑因素。每次填充散列表中的所有存储桶时,都需要分配新的存储空间并重新散列所有内容。如果您提前知道数据的大小,则可以避免这种情况。另一方面,平衡树根本不会遇到这个问题。
【讨论】:
但是操作的摊销成本仍然是O(1),所以你真的认为这是一个劣势吗? 在大多数情况下,可能不会。在实时应用中,是的。【参考方案5】:这是我能想到的:
-
有些数据不能被散列(或太昂贵而无法散列),因此不能存储在散列表中。
树按照您需要的顺序(排序)保存数据,而不是插入顺序。即使通过哈希表运行链表,您也无法(有效地)做到这一点。
树在最坏情况下的性能更好
【讨论】:
1.如果你不能产生一个哈希键,你如何产生一个键来确定节点在树中的位置?如果您可以为节点放置生成一个密钥,为什么不能将它用于哈希密钥? 2. 为什么不能用哈希表+链表有效地做到这一点?你能提供更多解释吗?请记住,链表本质上只是为排序优化的树。 3. 树有 log(N) 的最佳情况。散列总是不变的。碰撞对两种结构的影响相同。树如何才能在最坏情况下获得更好的性能? @Jake 1- 通过比较元素。有订单并不意味着你可以散列一些东西。 2-因为。 3- 没有什么可以在树上碰撞。 1.您要比较的项目是关键。所有二进制数据都可以通过某种方法进行哈希处理,计算机中的所有数据都是二进制数据。我想如果键很大,它可能会变得昂贵,但这可能会对树的比较功能产生相同的影响。 3. 搜索树有有序节点。仅当列表具有以某种形式对元素进行排序的一组键时,您才能对列表进行排序。如果两个节点有相同的键,那就是冲突。 1- 不正确。 3-键不正确:您不需要搜索树的任何键。如果插入与现有条目相等的条目,则不会影响性能,这与哈希冲突相反。了解原因是你的功课。【参考方案6】:以我的拙见,自平衡树作为学术主题非常有效。和我 不知道任何可以被限定为“的直接实现 红黑树”.
在现实世界中,记忆墙使它们的效率远低于纸上的。
考虑到这一点,哈希表是不错的替代品,尤其是在您不练习的情况下 他们是学术风格(忘记表格大小限制,你神奇地解决了 表格调整大小问题和几乎所有碰撞问题)。
一句话:保持简单。如果这对您来说很简单,那么对您的计算机来说也很简单。
【讨论】:
您能解释一下如何“忘记表格大小限制”吗?你只是建议我们忽略它,还是你的意思是别的? 你把C++标准库的实现算作学术话题吗?还是 Java 和 .NET 容器?如您所知,std::map 主要实现为平衡二叉搜索树;并且 gnu libstdc++ 将 std::map 实现为红黑树 IIRC。 "保持简单。如果这对您来说很简单,那么对您的计算机来说也很简单。"这不是一个很好的建议,想想斐波那契堆 - 高度复杂且非常有效。例如,SSS* 博弈树搜索或 Thorup 路径查找也是如此...... @Odrade -- 我认为他的意思是让你的表变得如此之大以至于不太可能发生冲突,而且也不用担心它有多满或多空(调整大小以节省内存)。跨度>以上是关于哈希表 v 自平衡搜索树的主要内容,如果未能解决你的问题,请参考以下文章