数组与链表

时间:2008-10-03 13:35:53

标签: arrays data-structures linked-list language-agnostic

为什么有人想在阵列上使用链接列表?

毫无疑问,对链接列表进行编码比使用数组要多一些工作,人们可能会想知道这些额外工作的合理性。

我认为在链表中插入新元素是微不足道的,但它是数组中的一项重要工作。使用链表存储一组数据与将其存储在数组中还有其他优点吗?

这个问题不是this question的重复,因为另一个问题是具体询问特定的Java类,而这个问题与一般数据结构有关。

34 个答案:

答案 0 :(得分:167)

另一个很好的理由是链表很适合高效的多线程实现。这样做的原因是更改往往是本地的 - 只影响一个或两个指针,以便在数据结构的本地化部分插入和删除。因此,您可以让许多线程在同一个链表上工作。更重要的是,可以使用CAS类型的操作创建无锁版本,并完全避免重锁。

使用链表,迭代器也可以在发生修改时遍历列表。在乐观的情况下,修改不会发生冲突,迭代器可以继续而不会发生争用。

使用数组,任何修改数组大小的更改都可能需要锁定数组的大部分内容,事实上,如果没有遍布整个数组的全局锁定,这种情况很少发生,因此修改就会停止世界事务。

答案 1 :(得分:143)

  • 在链表中存储不同大小的数据更容易。数组假定每个元素的大小完全相同。
  • 正如您所提到的,链表更容易有机增长。数组的大小需要提前知道,或者在需要增长时重新创建。
  • 改变链接列表只是改变指向什么的问题。混乱阵列更复杂和/或占用更多内存。
  • 只要您的迭代都发生在“foreach”上下文中,您就不会在迭代中失去任何性能。

答案 2 :(得分:122)

维基百科有很好的部分关于差异。

  

链接列表有几个优点   过阵列。可以插入元素   无限期地进入链表,而   一个数组最终会填满   向上或需要调整大小,价格昂贵   甚至可能没有的操作   如果内存碎片可能。   同样,一个阵列中有很多   元素被删除可能会成为   浪费空或需要制作   小。

     

另一方面,数组允许随机   访问,而链接列表只允许   对元素的顺序访问。   事实上,单链表只能   沿着一个方向穿过。这个   使链表不适合   看起来很有用的应用程序   按索引迅速提升元素,   比如heapsort。顺序访问   数组也比链接更快   因地点而在许多机器上列出   参考和数据缓存。关联   列表几乎没有收到任何好处   缓存。

     

链表的另一个缺点   是需要的额外存储空间   引用,经常使它们成为现实   对于小数据列表不切实际   诸如字符或布尔值之类的项目   值。它也可能很慢,并且   一个天真的分配者,浪费,到   为每个人分别分配内存   新元素,一般是一个问题   使用内存池解决。

http://en.wikipedia.org/wiki/Linked_list

答案 3 :(得分:55)

我将添加另一个 - 列表可以充当purely functional数据结构。

例如,您可以拥有完全不同的列表,共享相同的结尾部分

a = (1 2 3 4, ....)
b = (4 3 2 1 1 2 3 4 ...)
c = (3 4 ...)

即:

b = 4 -> 3 -> 2 -> 1 -> a
c = a.next.next  

无需将a指向的数据复制到bc

这就是为什么它们在功能语言中如此受欢迎,它使用不可变变量 - prependtail操作可以自由发生而无需复制原始数据 - 当你处理时非常重要的功能数据为不可变的。

答案 4 :(得分:28)

除了在列表中间插入更容易之外 - 从链表中间删除比从数组中删除更容易。

但坦率地说,我从未使用过链表。每当我需要快速插入和删除时,我也需要快速查找,所以我去了HashSet或Dictionary。

答案 5 :(得分:28)

合并两个链表(特别是两个双链表)比合并两个数组要快得多(假设合并具有破坏性)。前者取O(1),后者取O(n)。

编辑:为了澄清,我的意思是在无序的意义上“合并”,而不是在合并排序中。也许“连接”可能是一个更好的词。

答案 6 :(得分:17)

ArrayList和LinkedList的广泛不受重视的论点是 LinkedLists在调试时感到不舒服。维护开发人员花在理解程序上的时间,例如找到错误,增加和IMHO确实有时不能证明性能改进中的纳秒或企业应用程序中内存消耗的字节数。有时(好吧,当然这取决于应用程序的类型),最好浪费几个字节,但有一个更易于维护或更容易理解的应用程序。

例如,在Java环境中并使用Eclipse调试器,调试ArrayList将显示一个非常易于理解的结构:

arrayList   ArrayList<String>
  elementData   Object[]
    [0] Object  "Foo"
    [1] Object  "Foo"
    [2] Object  "Foo"
    [3] Object  "Foo"
    [4] Object  "Foo"
    ...

另一方面,观看LinkedList的内容并查找特定对象会成为一个Expand-The-Tree点击噩梦,更不用说过滤掉LinkedList内部所需的认知开销:

linkedList  LinkedList<String>
    header  LinkedList$Entry<E>
        element E
        next    LinkedList$Entry<E>
            element E   "Foo"
            next    LinkedList$Entry<E>
                element E   "Foo"
                next    LinkedList$Entry<E>
                    element E   "Foo"
                    next    LinkedList$Entry<E>
                    previous    LinkedList$Entry<E>
                    ...
                previous    LinkedList$Entry<E>
            previous    LinkedList$Entry<E>
        previous    LinkedList$Entry<E>

答案 7 :(得分:17)

首先,在C ++中,链接列表的使用不应该比使用数组更麻烦。您可以将std::listboost pointer list用于链接列表。链表和数组的关键问题是指针和可怕的随机访问所需的额外空间。如果您

,您应该使用链接列表
  • 您不需要随机访问数据
  • 您将添加/删除元素,尤其是在列表中间

答案 8 :(得分:14)

对我来说就是这样,

  1. 访问

    • 关联列表仅允许对元素进行顺序访问。因此,算法复杂度是O(n)
    • 的阶数
    • 数组允许随机访问其元素,因此复杂性为O(1)的顺序
  2. 存储

    • 链接列表需要额外的存储空间以供参考。这使得它们对于诸如字符或布尔值的小数据项列表不实用。
    • 数组不需要额外的存储空间来指向下一个数据项。可以通过索引访问每个元素。
  3. 尺寸

    • 链接列表的大小本质上是动态的。
    • 数组的大小仅限于声明。
  4. 插入/缺失

    • 可以无限期地在链接列表中插入和删除元素。
    • 插入/删除数组中的值非常昂贵。它需要重新分配内存。

答案 9 :(得分:11)

两件事:

  

毫无疑问,对链接列表进行编码比使用数组要多一些工作,并且他想知道什么是合理的额外工作。

使用C ++时,切勿对链表进行编码。只需使用STL。实施起来有多难,绝不应该成为选择一种数据结构而不是另一种数据结构的理由,因为大多数数据结构已在那里实现。

至于数组和链表之间的实际差异,对我来说最重要的是你如何计划使用该结构。我将使用术语向量,因为这是C ++中可调整大小的数组的术语。

索引到链接列表很慢,因为你必须遍历列表才能到达给定的索引,而向量在内存中是连续的,你可以使用指针数学到达那里。

附加到链接列表的末尾或开头很容易,因为您只需更新一个链接,在向量中您可能需要调整大小并复制内容。

从列表中删除项目很简单,因为您只需断开一对链接然后将它们重新连接在一起即可。从矢量中移除项目可以更快或更慢,具体取决于您是否关心订单。将最后一个项目交换到您要删除的项目上方更快,而在向下移动后的所有内容更慢但仍保留订购。

答案 10 :(得分:10)

Eric Lippert最近有一个post因为保守地使用数组的原因之一。

答案 11 :(得分:8)

快速插入和删除确实是链接列表的最佳参数。如果您的结构动态增长并且不需要对任何元素进行恒定时间访问(例如动态堆栈和队列),则链接列表是一个不错的选择。

答案 12 :(得分:7)

没有人再编码自己的链表了。那太傻了。使用链表需要更多代码的前提是错误的。

现在,构建链表只是学生的练习,因此他们可以理解这个概念。相反,每个人都使用预建列表。在C ++中,基于我们问题中的描述,这可能意味着一个stl向量(#include <vector>)。

因此,选择链接列表与数组完全关于权衡每个结构相对于应用需求的不同特征。克服额外的编程负担应该对决策没有任何影响。

答案 13 :(得分:7)

链接列表在集合不断增长时特别有用。萎缩。例如,很难想象尝试使用数组实现队列(添加到最后,从前面删除) - 你将花费所有时间来减少事情。另一方面,链接列表很简单。

答案 14 :(得分:7)

除了在列表中间添加和删除之外,我更喜欢链接列表,因为它们可以动态增长和缩小。

答案 15 :(得分:7)

这是一个快速的:删除项目更快。

答案 16 :(得分:6)

这实际上是效率的问题,插入,移除或移动(不是简单地交换)链接列表中的元素的开销是最小的,即操作本身是O(1),对于O(n)数组。如果您在数据列表上运行很多,这可能会产生显着差异。您根据操作方式选择了数据类型,并为您正在使用的算法选择最有效的数据类型。

答案 17 :(得分:6)

数组在确切知道项目的确切数量以及按索引搜索有意义的地方是有意义的。例如,如果我想在没有压缩的情况下在给定时刻存储视频输出的确切状态,我可能会使用大小为[1024] [768]的数组。这将为我提供我所需要的,并且列表在获得给定像素的值时要慢得多。在数组没有意义的地方,通常有比列表更好的数据类型来有效地处理数据。

答案 18 :(得分:6)

阵列与链接列表:

  1. 由于内存碎片,数组内存分配有时会失败。
  2. 在数组中缓存更好,因为所有元素都分配了连续的内存空间。
  3. 编码比数组更复杂。
  4. 链接列表没有大小限制,与阵列
  5. 不同
  6. 在链接列表中插入/删除速度更快,并且在数组中访问速度更快。
  7. 从多线程角度更好地链接列表。

答案 19 :(得分:3)

链接列表比数组更需要维护开销,它还需要额外的内存存储,所有这些点都是一致的。但有一些事情无法做到。在许多情况下,假设您想要一个长度为10 ^ 9的数组,您无法获得它,因为必须存在一个连续的内存位置。链接列表可能是这里的救世主。

假设您希望使用数据存储多个内容,则可以在链接列表中轻松扩展它们。

STL容器通常在场景后面有链表实现。

答案 20 :(得分:3)

链接列表

关于插入更为可取!基本上它的作用是处理指针

1 - &gt; 3 - &gt; 4

插入(2)

1 ........ 3 ...... 4
..... 2

最后

1 - &gt; 2 - &gt; 3 - &gt; 4

3点2点一箭,2点1点箭头

简单!

但是来自Array

| 1 | 3 | 4 |

插入(2) | 1 | 3 | | 4 | | 1 | | 3 | 4 | | 1 | 2 | 3 | 4 |

任何人都可以想象出差异! 仅为4指数我们正在执行3个步骤

如果阵列长度是一百万呢?阵列有效吗? 答案是不! :)

删除同样的事情! 在Linked List中,我们可以简单地使用指针并使元素无效,然后在对象类中使用! 但是对于数组,我们需要执行shiftLeft()

希望有所帮助! :)

答案 21 :(得分:3)

1-链接列表是一种动态数据结构,因此它可以在运行时通过分配和释放内存来增长和收缩。因此,无需给出链表的初始大小。节点的插入和删除确实更容易。

链接列表的

2-大小在运行时可以增加或减小,因此不会浪费内存。在数组的情况下,会浪费很多内存,例如,如果我们声明一个大小为10的数组并在其中仅存储6个元素,那么就会浪费4个元素的空间。链表中没有这样的问题,因为仅在需要时才分配内存。

3-使用链接列表可以轻松实现堆栈和队列之类的数据结构。

答案 22 :(得分:3)

在数组中,您有权在O(1)时间内访问任何元素。因此它适用于二进制搜索快速排序等操作。另一方面,链接列表适合于在O(1)时间内插入删除。两者都有优点和缺点,并且优先选择其中一个,而不是你想要实现的目标。

- 更大的问题是我们可以将两者混合使用。类似于python和perl实现的列表。

答案 23 :(得分:3)

答案 24 :(得分:3)

因为数组本质上是静态的,因此所有操作都是如此 比如在编译时发生内存分配 只要。因此,处理器必须在运行时减少工作量。

答案 25 :(得分:2)

只使用链表的原因是插入元素很容易(也可以删除)。

Disadvatige可能是指针占用了很多空间。

关于编码更难: 通常,您不需要包含代码链接列表(或仅一次) STL  如果你仍然必须这样做,那就不那么复杂了。

答案 26 :(得分:1)

根据您的语言,可以考虑以下一些缺点和优势:

C编程语言:使用链表时(通常通过结构指针),必须特别注意不要泄漏内存。正如前面提到的,链接列表很容易改变,因为所有人都在改变指针,但我们是否会记得释放一切?

Java :Java有一个自动垃圾收集,因此泄漏的内存不会成为问题,但是高级程序员隐藏的是链接列表的实现细节。从列表中间删除节点等方法比某些语言用户所希望的更复杂。

答案 27 :(得分:1)

为什么列表上的链表?正如一些人已经说过的那样,插入和删除的速度更快。

但也许我们不必忍受两者的限制,并同时充分利用两者......呃?

对于数组删除,您可以使用“已删除”字节来表示已删除行的事实,因此不再需要重新排列数组。为了减轻插入或快速更改数据的负担,请使用链接列表。然后在提到它们时,先让你的逻辑搜索一个,然后再搜索另一个。因此,将它们组合使用可以让您获得最佳效果。

如果你有一个非常大的数组,你可以将它与另一个更小的数组或链表结合起来,其中较小的数组包含最近使用的20,50,100个项目。如果所需的不在较短的链表或数组中,则转到大数组。如果在那里找到,你可以将它添加到较小的链表/数组中,假设“最近使用的东西最可能被重复使用”(是的,可能会从列表中碰到最近最少使用的项目)。在许多情况下都是如此,并解决了我必须在.ASP安全权限检查模块中解决的问题,轻松,优雅,速度快。

答案 28 :(得分:1)

我还认为链接列表比数组更好。 因为我们在链接列表中进行遍历而不是在数组中进行遍历

答案 29 :(得分:1)

虽然你们中的许多人已经触及了链表和阵列的主要优点,但大多数比较都是如何比另一个更好/更差.Eg。您可以在数组中进行随机访问,但在链表和其他链接中不可能。但是,假设链接列表和数组将应用于类似的应用程序中。但是,正确的答案应该是链接列表在特定应用程序部署中优先于阵列,反之亦然。 假设您要实现字典应用程序,您会使用什么? 数组:mmm它可以通过二进制搜索和其他搜索算法轻松检索..但让我们想想链接列表如何更好。想要在字典中搜索“Blob”。有一个链接列表是否有意义A-&gt; B-&gt; C-&gt; D ----&gt; Z然后每个列表元素也指向一个数组或以该字母开头的所有单词的另一个列表..

A -> B -> C -> ...Z
|    |    |
|    |    [Cat, Cave]
|    [Banana, Blob]
[Adam, Apple]

现在上面的方法更好还是[Adam,Apple,Banana,Blob,Cat,Cave]的平面阵列?甚至可以用阵列吗? 因此链接列表的一个主要优点是,您可以拥有一个元素,不仅指向下一个元素,还指向其他链接列表/数组/堆/或任何其他内存位置。 数组是一个单独的连续存储器,切成它要存储的元素的块大小。另一方面,链接列表是一块非连续的存储单元(可以是任何大小,可以存储任何东西)并指向每个其他你想要的方式。 同样地,假设您正在制作USB驱动器。您现在要将文件保存为任何数组还是链接列表?我想你明白了我的意思:)

答案 30 :(得分:1)

  

为什么有人想在阵列上使用链接列表?

这只是一个原因 - 如果您需要链接列表数据结构和您正在使用的编程语言不支持指针。

答案 31 :(得分:1)

除了方便插入和删除外,链表的内存表示形式与数组不同。链表中元素的数量没有限制,而在数组中,您必须指定元素的总数。 查看this文章。

答案 32 :(得分:0)

使用链接列表的人必须阅读。人们会再次爱上阵列。 它谈到了 乱序执行,硬件预取,内存延迟等。

http://www.futurechips.org/thoughts-for-researchers/quick-post-linked-lists.html

答案 33 :(得分:0)

数组和链接列表之间的区别在于,数组是基于索引的数据结构,每个元素都与索引相关联,而链接列表是使用引用的数据结构,每个节点都引用另一个节点。数组的大小是固定的,而链接列表的大小是固定的。