使用链表而不是数组的具体规则是什么?


18

当您希望廉价地插入和删除元素,并且在内存中元素彼此不紧要时,可以使用链表。

这是非常抽象的,我想具体解释为什么应该使用链表而不是数组。我没有编程方面的经验,所以我没有太多(如果有)实际经验。


3
老实说,除非您有编写某些东西的经验,然后看到更改数据结构对代码的影响,否则我认为示例对您没有太大意义。尝试编写您感兴趣的内容,找出最适合的数据结构和容器,或者查看一些现有代码,思考为什么选择每个容器以及对其进行更改会有什么影响。
2012年

我只是认为,如果没有比此处合理的形式更长的工作示例,则无法将数据结构选择的折衷和副作用传达给(没有经验的)OP,这是没有用的。给出的示例可以很好地回答问题,并且可以按要求回答问题,但不是(我理解为)暗示的实际理解要求。
没用的2012年

Answers:


37

这是示例与类比之间的某种方式。您需要做一些差事,因此您要拿一张纸并写下:

  • 银行
  • 杂货
  • 干洗

然后您会记住,您还需要购买邮票。由于您所在城镇的地理位置,您需要在银行之后进行此操作。您可以将整个列表复制到一张新纸上:

  • 银行
  • 邮票
  • 杂货
  • 干洗

或者您可以在您拥有的那一个上随意涂写:

  • 银行.......邮票
  • 杂货
  • 干洗

当您想到其他差事时,可以将它们写在列表的底部,但是带有箭头的提示会提醒您自己按什么顺序进行操作。这是一个链接列表。与每次添加内容时复制整个列表相比,它更快更容易。

然后,当您在银行时,手机响了“嘿,我拿到邮票了,别再捡了”。您只需将STAMPS从列表中剔除,就不会重写没有STAMPS的全新文件。

现在,您实际上可以在代码中实现一个差事清单(也许是一个根据您的地理位置将您的差事排序的应用程序),并且您很有可能实际上会在代码中使用一个链接清单。您想添加和删除很多项目,而订单很重要,但是您不想在每次插入或删除后重新复制整个列表。



@Dennis是的,我知道,这要归功于移动语义。但是,并非所有语言都适用,因此该示例有效。
凯特·格雷戈里

1
@KateGregory足够公平,但还是要指出,“基本”数据结构通常比我们在学校(或其他地方)学到的酷数据结构要好。我不希望新毕业生在任何地方都使用LL,因为从理论上讲它是适当的,但实际上可能是一个糟糕的选择。使用正确的数据结构很重要,有时人们会使其过于复杂。乔纳森·布鲁(Jonathan Blow)(“辫子”的创建者)关于数据结构的演讲稍有关联。
丹尼斯

1
@Ray:如果您希望链表的个数大约为4个元素,并且比较相对便宜,则链表的性能会优于树形结构。我不确切地知道临界点在哪里,情况并非如此。同样,树结构要求您的数据可以排序,而列表结构则允许您维护插入顺序(或任何任意顺序)。
大卫·斯通

2
我认为这种比喻不是很准确。作为人类,我们可以(至少对于这样的简短列表而言)在O(1)中找到一个元素。但是,如果要在链表中进行随机插入,则必须平均遍历一半的元素,这只会花费O(n)来找到要插入的位置。这样,实际插入仅花费O(1),但使用数组,您的花费也是“仅” O(n)。因此,原因不仅在于移动语义,还在于算法。但是,由于不连续的内存访问,对于喜欢的列表,由big-O隐藏的常量因子要大得多。
5gon12eder


12

C语言“调用堆栈” 在x86(和大多数其他)二进制API中作为链接列表实现

也就是说,C语言过程调用遵循先进先出的原则。执行(可能是递归的)函数调用的结果称为“调用堆栈”,有时甚至称为“堆栈”。

CALLx86指令结束implmenting使用“调用堆栈”的链接列表。甲CALL指令推%EIP寄存器的内容,该指令的地址CALL入堆栈存储器。被调用函数序言将%EBP寄存器的内容(调用函数中局部变量的最低地址)压入堆栈存储器。然后,被调用的函数序言将%EBP设置为当前函数的堆栈库。

这意味着%EBP是指向存储位置的指针,该存储位置保存调用函数的%EBP值的地址。只是链表,部分通过硬件实现CALL

就此的好处而言,这就是x86 CPU实现函数调用的方式,尤其是在函数具有自身参数副本和函数本地变量的函数调用。每个函数调用都会在“调用堆栈”上推送一些信息,这些信息使CPU能够从调用函数中遗留的地方进行接取,而不会受到被调用函数或调用函数的干扰。


3

链表可用于实现消息队列。

消息队列是一种结构,我们在其中存储有关事件的信息以供以后处理。例如,当用户按下键或移动鼠标时,这就是事件。事件发生时,应用程序可能很忙,因此不能期望它在事件发生的确切时刻进行处理。因此,将事件放置在消息队列中(有关按下哪个键或鼠标移至何处的信息),并且当应用程序有空闲时间时,它将检查其消息队列,从中获取事件并进行处理他们。(这发生在毫秒的时间范围内,因此并不明显。)

从我刚刚描述的使用场景来看,很明显,我们从不关心随机访问消息队列中存储的事件;我们只关心能够在其中存储消息并进行检索。因此,使用链接列表可以提供最佳的插入/删除时间,这是很有意义的。

(请不要指出,使用循环数组列表可能会或可能会或几乎会使用循环数组列表来实现消息队列;这是技术细节,并且有一个局限性:您只能存储其中的邮件数量有限。)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.