为什么追加到列表不好?


69

我最近开始学习scala,并且遇到了::(cons)函数,该函数位于列表的前面。
在《 Scala中的编程》一书中,它指出没有附加功能,因为附加到列表的性能为o(n),而前置的性能为o(1)

关于那句话,我有些不对劲。

性能不取决于实现吗?难道不能简单地实现带有向前和向后链接的列表并将第一个和最后一个元素存储在容器中?

我想的第二个问题是,当我有一个列表1,2,3并想在其末尾加4时该怎么办?


4
“性能不取决于实现吗?”:不要忘记这List是Scala中的一个具体类,与JavaList接口无关。
krookedking

Answers:


80

关键是该x :: somelist变量不会突变somelist,而是创建一个新列表,该列表包含x后跟的所有元素somelist。这可以在O(1)时间内完成,因为您只需要在新创建的单链接列表中将设置somelist为的后继即可x

如果改为使用双向链表,则x还必须将其设置为somelisthead的前身,后者会进行修改somelist。因此,如果我们希望能够::在O(1)中进行操作而无需修改原始列表,则只能使用单链接列表。

关于第二个问题:您可以用来:::将单元素列表连接到列表的末尾。这是O(n)操作。

List(1,2,3) ::: List(4)

2
的复杂度是:::多少?
2011年

6
@Jus:的运行时复杂度为:::O(n)其中n第一个列表的长度。因此,您绝对不应该循环执行此操作。
sepp2k 2011年

21

其他答案也为这种现象提供了很好的解释。如果要在子例程的列表中添加许多项,或者通过添加元素来创建列表,则功能性的习惯用法是以相反的顺序构建列表,将项目放在列表的前面,然后最后将其反转。这使您获得O(n)的性能,而不是O(n²)。


14

由于问题是刚刚更新的,因此值得注意的是这里的情况已经改变。

在当今的Scala中,您可以简单地使用xs :+ x在任何顺序集合的末尾附加一个项目。(还有x +: xs前面加上该助记符许多Scala的2.8+收集操作的是,在山坳上的信息放进旁边的山坳经文。)

使用默认链接实现或时,将为O(n),但是如果使用或,则这将是有效的恒定时间。Scala可能是Scala最有用的类似列表的集合,这与Java如今已经几乎没有用的列表不同。ListSeqVectorIndexedSeqVectorVector

如果您使用的是Scala 2.8或更高版本,则必须绝对阅读Collections简介


12

挂起更快,因为它只需要执行两个操作:

  1. 创建新的列表节点
  2. 让该新节点指向现有列表

追加需要更多操作,因为您必须遍历列表的末尾,因为您只有头的指针。

我以前从未在Scala中进行编程,但是您可以尝试使用列表缓冲区


请注意,为了 遍历末尾,可能会有一个指向末尾的指针。但是,您将不得不在最后一个元素中更改指针。如在其他答案中提到的那样,这将需要进行进一步的操作(例如,元素的副本),因为List它在Scala中是不可变的
何塞·安迪亚斯(JoséAndias)'18年

6

大多数功能语言都是单链列表数据结构,因为它是一种方便的不可变集合类型。用功能语言说“列表”时,通常就是您的意思(单链接列表,通常是不可变的)。对于这种类型,append为O(n),而cons为O(1)。

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.