【问题标题】:Why are linked lists faster than arrays?为什么链表比数组快?
【发布时间】:2011-07-23 15:22:56
【问题描述】:

我对此感到非常困惑。到处都写着“链表比数组快”,但没有人努力说出为什么。使用简单的逻辑我无法理解链接列表如何更快。在一个数组中,所有单元格都彼此相邻,因此只要您知道每个单元格的大小,就很容易立即到达一个单元格。例如,如果有一个 10 个整数的列表,并且我想获取第四个单元格中的值,那么我就直接转到数组的开头+24 个字节并从那里读取 8 个字节。

另一方面,当你有一个链表并且你想在第四位获取元素时,你必须从列表的开头或结尾开始(取决于它是单列表还是双列表)然后去从一个节点到另一个节点,直到找到所需的内容。

那么如何一步一步地比直接进入一个元素更快呢?

【问题讨论】:

  • 这取决于您要完成的工作。如果要搜索或访问特定元素,数组会更快,但如果要插入或删除元素,链表会更快。
  • 我非常想看到一个链接到某个地方,使这个声明。
  • @x3ro 正如一些人回答的那样,快速是相关的。所以在某些地方写到数组比链表快,在某些地方写到链表比数组快。我用谷歌搜索并没有找到任何结果,这就是我在这里发布问题的原因,因为我很困惑。现在,我的问题居于首位,任何想知道同样问题的人都会明白自己的想法。
  • @Pithikos:en.wikipedia.org/wiki/… 呢?
  • @x3ro 作为初学者,我更喜欢一个更直截了当的答案,而不是阅读整个维基百科却什么都不懂。毕竟不是每个人都对格式语言感到满意。

标签: arrays performance linked-list


【解决方案1】:

这个问题的标题具有误导性。

它断言链表比数组快,而没有很好地限制范围。有很多次数组可以显着更快,也有很多次链表可以显着更快:链接的特殊情况似乎不支持“更快”的列表。

有两点需要考虑:

  1. 链表与数组的理论界限在特定操作中;和
  2. 现实世界的实现和使用模式,包括缓存局部性和分配。

就索引元素的访问而言:操作是数组中的O(1),正如所指出的,它非常快(只是一个偏移量)。 操作是链表中的O(k)(其中k 是索引,并且可能始终是<< n,具体取决于)但是如果链表是已经被遍历那么这是O(1)每一步,它与数组“相同”。数组遍历 (for(i=0;i<len;i++) 是否更快(或更慢)取决于特定的实现/语言/运行时。

但是,如果存在 特定 的情况,即数组对于上述任何一种操作(查找或遍历)都不是更快,那么看看 被更多地剖析会很有趣详情。 (我确信有可能找到一种语言在列表上实现非常退化的数组cough Haskell cough

编码愉快。


我的简单用法总结:数组适用于索引访问和涉及交换元素的操作。然而,非摊销的重新调整大小操作和额外的松弛(如果需要)可能相当昂贵。链接列表分摊了重新调整大小(并用松弛换取每个单元格的“指针”),并且通常可以擅长“删除或插入一堆元素”等操作。最后,它们是不同的数据结构,应该这样对待。

【讨论】:

    【解决方案2】:

    就像编程中的大多数问题一样,上下文就是一切。您需要考虑数据的预期访问模式,然后适当地设计您的存储系统。如果您插入一次,然后访问它 1,000,000 次,那么谁在乎插入成本是多少?另一方面,如果您在阅读时经常插入/删除,那么这些成本会推动决策。

    【讨论】:

      【解决方案3】:

      取决于您所指的操作。在链表中添加或删除元素比在数组中快得多。

      在链表和数组中按顺序逐一迭代的速度或多或少是相同的。

      在数组中间获取一个特定元素要快得多。

      而且数组可能会浪费空间,因为在扩展数组时,分配的元素通常比当时需要的要多(想想 Java 中的 ArrayList)。

      所以你需要根据你想做什么来选择你的数据结构:

      许多插入和顺序迭代 --> 使用 LinkedList

      随机访问和理想的预定义大小 --> 使用数组

      【讨论】:

      • 关于顺序迭代,链表可能会稍微超出数组。您断言在列表上按顺序迭代或多或少相同的速度可能是正确的,但在现代计算机上可能难以测量。我相信在最纯粹的情况下,链表在迭代速度上优于数组,因为移动到下一个节点不需要计算下一个内存偏移量的地址,就像数组一样,因为链表将地址保存到下一个节点。
      • 话虽如此,计算下一个元素的位置并访问它是一个简单的添加和访问可能已经在缓存中的内存;对于链表,它需要指针追踪,这通常很慢。无论如何,计算机通常都有高效的寻址模式来进行数组索引。
      【解决方案4】:

      因为在数组中间插入时不会移动内存。 对于您提出的情况,它是真实的 - 数组更快,您只需要算术就可以从一个元素到另一个元素。链表需要间接和碎片内存。 关键是要知道使用什么结构以及何时使用。

      【讨论】:

      • 值得注意的是,INSERTION 更快(删除也是如此),但不是索引访问,这是 Pithikos 混淆的根源。
      • 所以你的意思是在有一个完整数组并且你想添加一个新元素的情况下?然后确保您必须调整整个数组的大小。但是如果数组是空的呢?
      • 对于简单的访问,没有什么比数组更好的了。问题是“为什么链表比数组快?”答案解决了这个问题。在实际应用中,您对数组进行了许多操作(考虑排序情况),这就是为什么比数组更频繁地使用的原因;
      • @Pithikos 在单个链表中插入一个元素 -> 只有一个位置。在 std:vector (array) 中插入一个元素可能重新分配内存并移动所有元素 -> 时间不是恒定的。可能更小或更大。
      【解决方案5】:

      在以下情况下,链表优于数组:

      a) 您需要从列表中进行恒定时间的插入/删除(例如在时间可预测性绝对关键的实时计算中)

      b) 您不知道列表中有多少项目。使用数组,如果数组变得太大,您可能需要重新声明和复制内存

      c) 你不需要随机访问任何元素

      d) 你希望能够在列表中间插入项目(例如优先级队列)

      在以下情况下最好使用数组:

      a) 您需要对元素进行索引/随机访问

      b) 提前知道数组中元素的数量,以便为数组分配正确的内存量

      c) 在按顺序遍历所有元素时,您需要速度。您可以在数组上使用指针数学来访问每个元素,而您需要根据链表中每个元素的指针来查找节点,这可能会导致页面错误,从而导致性能下降。

      d) 内存是一个问题。填充数组比链表占用更少的内存。数组中的每个元素只是数据。每个链表节点都需要数据以及一个(或多个)指向链表中其他元素的指针。

      数组列表(如 .Net 中的那些)为您提供数组的好处,但为您动态分配资源,因此您无需过多担心列表大小,您可以毫不费力地删除任何索引处的项目或重新洗牌周围的元素。在性能方面,arraylists 比原始数组慢。

      参考: 拉马尔回答 https://stackoverflow.com/a/393578/6249148

      【讨论】:

        猜你喜欢
        • 2015-06-29
        • 2012-08-14
        • 1970-01-01
        • 1970-01-01
        • 2010-12-07
        • 1970-01-01
        • 2020-11-17
        • 2012-12-03
        • 1970-01-01
        相关资源
        最近更新 更多