【问题标题】:linked list vs arrays for dictionaries字典的链表与数组
【发布时间】:2014-10-14 23:00:44
【问题描述】:

我最近在一次采访中被问及advantages and disadvantages of linked list and arrays for dictionary of words implementationwhat is the best data structure for implementing it? 这是我搞砸的地方。谷歌搜索后,我无法明确找到特定于字典的确切答案,而是一般的链表 v 数组解释。 上述问题的最佳答案是什么?

【问题讨论】:

    标签: javascript algorithm dictionary data-structures analysis


    【解决方案1】:

    没有一个答案。

    如果您只想查找单个项目,则两个明显的选择是基于哈希表,或者如果您想查找项目范围,则基于平衡树。

    如果您进行大量搜索并进行相对较少的插入或删除,则排序数组可以很好地工作。找到首选链表的情况相当困难。根据具体情况(尤其是查找所有以“ste”开头的单词之类的事情),尝试也可以非常好地工作(并且通常在最小化给定数据集所需的存储方面也做得很好)。

    不过,这些都是非常广泛的类别,而不是具体的实现。还有一些变体,例如可扩展哈希和分布式哈希表,它们在特定情况下可能很有用(并且还具有一些类似树的属性,因此基于范围的搜索之类的东西可能相当有效)。

    【讨论】:

      【解决方案2】:

      如果您只是将它用于查找,那么数组显然是两者中的最佳选择。您可以从 O(n log n) 中的单词列表构建字典——只需构建一个数组并对其进行排序。查找是 O(log n) 的二进制搜索。

      虽然您可以在 O(n) 中构建一个单词的链表,但平均而言,查找需要您查看 n/2 个单词。差别相当大。给定一个 128K 单词的英语词典,链表查找平均需要 64,000 次字符串比较。二分搜索最多需要 17 个。

      另外,n个单词的链表会比n个单词的数组占用更多的内存,因为链表中需要next指针。

      如果您需要更新字典的能力,如果与查找相比更新不频繁(几乎可以肯定是这种情况),您可能仍希望使用数组。我想不出一个真实世界的例子,即更新频率高于查询频率的单词词典。

      正如其他人所指出的,数组和链表都不是字典的最佳选择。但是在您给出的两个选项中,数组几乎在所有情况下都更胜一筹。

      【讨论】:

        【解决方案3】:

        实现字典的最佳数据结构是suffix trees。你也可以看看tries

        【讨论】:

          【解决方案4】:

          好吧,如果您要构建字典,您会希望它是一个排序结构。所以你要使用排序数组或排序链表。

          • 对于链表检索是O(n),因为您必须检查所有单词,直到找到您需要的单词。对于已排序的数组,您可以使用二进制搜索找到正确的位置,即O(log n)
          • 对于已排序的数组,insertionO(log n) 以找到正确的位置(二进制搜索),然后是 O(n) 以插入,因为您需要将所有内容向下推。对于链表,查找位置是O(n),然后插入是O(1),因为您只需要调整指针。 删除也是如此。

          由于您不会过多地更新字典,您可以在O(nlog n) 时间构建并排序数组(例如使用快速排序)。之后,使用二分搜索查找O(log n)。此外,正如下面提到的delnan,使用数组的优点是您访问的所有内容在内存中都是顺序的;即,数据是本地化的(参考位置)。这最大限度地减少了缓存未命中(这是昂贵的)。使用链表,数据分散在各处,无法保证它们紧密相连,这增加了缓存未命中的机会。考虑到这一点,考虑到这两个选项,请使用 array

          如果您使用红黑树实现排序的哈希图(您的树条目,即键可以与哈希图耦合),您可以做得更好;这里搜索、插入和删除是O(log n)。但这实际上取决于您的行为特征;如果您只进行查找,最好使用简单的哈希图(O(1) 检索)。

          您可以使用的另一个有趣的数据结构是Trie,其中插入和查找是O(m)m 是字符串的长度。

          【讨论】:

          • 您的这一点For an array, insertion is O(n) to find the right location and then O(n) to insert because you need to push everything down. For a linked list, it would be O(n) to find the location and then O(n) to insert because you only have to adjust pointers. 有点令人困惑。 O(n) 用于链表和数组查找正确的位置和插入?两者都有 O(n)?能详细点吗?
          • @26ph19 抱歉,打错了!应该是O(1);编辑。
          • 我真的不明白你怎么能断定链表比数组更好,至少从你提供的事实来看。渐近复杂度完全相同。找到要插入的位置后的实际列表操作是 O(1) 并不重要,因为插入是 O(n)数组胜出。
          • @delnan 我实际上对那部分有点不确定。你能解释一下常数因素是如何胜出的吗?假设你总是在列表的中间插入一些东西,你会得到O(n/2) + O(1),而对于一个数组,它会变成O(n) + O(n)。请让我知道我是如何搞砸的,以便我更正答案!
          • 你给出的渐近界是正确的(实际上数组在相同的假设下应该是O(n/2) + O(n)),并且链表的常数因子包括1/2和数组的常数因子包括2个左右。但除此之外,还有许多其他不变的因素。原则上,链表通常会导致大量缓存未命中,而数组(按顺序遍历)会命中缓存。理论和实证实验都一次又一次地表明,即使对于相当大的数据集,数组也会胜出。
          猜你喜欢
          • 2011-02-10
          • 2022-08-16
          • 1970-01-01
          • 2012-04-05
          • 2010-09-15
          • 1970-01-01
          • 1970-01-01
          • 2018-07-06
          • 1970-01-01
          相关资源
          最近更新 更多