【发布时间】:2020-10-03 13:54:22
【问题描述】:
您可能遇到过提到在 hashmap/dictionary/table 中查找元素比在 list/array 中查找元素更快的地方。我的问题是为什么?
(到目前为止我做出的推论:为什么它应该更快,据我所知,在这两种数据结构中,它必须遍历直到到达所需的元素)
【问题讨论】:
标签: arrays list search hashmap time-complexity
您可能遇到过提到在 hashmap/dictionary/table 中查找元素比在 list/array 中查找元素更快的地方。我的问题是为什么?
(到目前为止我做出的推论:为什么它应该更快,据我所知,在这两种数据结构中,它必须遍历直到到达所需的元素)
【问题讨论】:
标签: arrays list search hashmap time-complexity
要理解这一点,您可以考虑元素是如何存储在这些数据结构中的。
HashMap/Dictionary 如您所知,它是一个键值数据结构。要存储元素,首先要找到哈希值(始终为键提供唯一值的函数。例如,可以通过模运算来制作简单的哈希函数。)。然后你基本上把这个值放在这个散列键上。
在 List 中,您基本上一直将元素附加到末尾。元素插入的顺序在这个数据结构中很重要。分配给这个数据结构的内存不是连续的。
在Array中,你可以认为它类似于List。但是在这种情况下,分配的内存本质上是连续的。所以,如果你知道第一个索引的地址值,你就可以找到第n个元素的地址。
现在考虑从这些数据结构中检索元素:
来自 HashMap/Dictionary: 当您搜索一个元素时,您要做的第一件事就是找到该键的哈希值。一旦你有了它,你就去地图上寻找散列值并获得这个值。在这种方法中,执行的操作量始终是恒定的。在渐近符号中,这可以称为 O(1)。
来自列表:您实际上需要遍历每个元素并检查该元素是否是您要查找的元素。在最坏的情况下,您想要的元素可能出现在列表的末尾。因此,执行的操作量会有所不同,在最坏的情况下,您可能必须迭代整个列表。在渐近符号中,这可以称为 O(n)。其中 n 是列表中元素的数量。
从数组:要在数组中查找元素,你需要知道的是第一个元素的地址值。对于任何其他元素,您可以计算该元素相对于第一个索引的存在程度。
例如,假设第一个元素的地址值为100。每个元素占用4字节的内存。您要查找的元素位于第 3 位。然后你知道这个元素的地址值是 108。使用的数学是
Addresses of first element + (position of element -1 )* memory used for each element。
即 100 + (3 - 1)*4 = 108。
在这种情况下,您也可以观察到执行的操作始终是查找元素的常量。在渐近符号中,这可以称为 O(1)。
现在比较一下,O(1) 总是比 O(n) 快。因此从 HashMap/Dictionary 或数组中检索元素总是比 List 更快。
我希望这会有所帮助。
【讨论】:
让我们通过类比来推理。假设你想找一件特定的衬衫在早上穿。我认为,这样做时,您不必逐字查看您拥有的每一件衣服。相反,您可能会做一些事情,例如检查梳妆台中的特定抽屉或壁橱的特定部分,然后只看那里。毕竟,你不会(我希望)在袜子抽屉里找到你的衬衫。
哈希表比列表搜索得更快,因为它们采用了类似的策略——它们根据每个项目都有一个“应该”在的位置的原则组织数据,然后通过查找该位置来搜索项目。将此与列表进行对比,列表中的项目是根据添加顺序进行组织的,并且没有特定模式来说明每个项目所在位置的特定模式。
更具体地说:实现哈希表的一种常见方法是使用称为链式哈希的策略。这个想法是这样的:我们维护一个 buckets 数组。然后我们提出一个规则,为每个对象分配一个桶号。当我们向表中添加一些东西时,我们确定它应该去哪个桶号,然后跳转到那个桶,然后把项目放在那里。要搜索一个项目,我们确定桶号,然后跳转到那里,只查看该桶中的项目。假设我们用来分配项目的策略最终将项目或多或少均匀地分布在桶中,这意味着我们在搜索时不必查看哈希表中的大多数项目,这就是为什么哈希表的搜索速度往往比列表快得多。
有关这方面的更多详细信息,请查看these lecture slides on hash tables,其中填写了有关如何完成的更多详细信息。
希望这会有所帮助!
【讨论】: