为什么无序的链表?这是无序结构的数组/ ll功能的良好平衡吗?

时间:2014-02-17 14:00:33

标签: arrays data-structures linked-list

如果这被认为是偏离主题的话我很抱歉,但我希望数据结构的设计与编程实践的相关性足以与此相关。

我正在学习C和C ++,并且已经介绍了数组和内存分配,我们开始查看链表。讲师表示他们的优势主要是能够在不复制整个结构的情况下添加/删除节点 - 我可以看到,为了将节点添加到数组,我们需要免费的existing_array_size + node_size 记忆,尽管我们将删除整个existing_array_size。即使删除条目,我们也可能想要再次复制整个结构。

使用链表,我们支付执行时间的代价(在遍历潜在的n个节点时只需查找和删除一个),以便提高内存效率。

val of ll0
ptr to ll1
...
val of ll1
ptr to ll2
...
...
val of lln
null ptr

一切都很好。但是,我想到的是什么(可能是由于它是全新的而且没有正确理解,请原谅我)是我们为n+1指针者浪费了大量的记忆(我预计这是关于这是否真的是一个'浪费'..它是方法所必需的,但不包含任何实际的)。

对我来说似乎更明显的结构 尤其是无序列表的 如下所示,取一个ptr to start

(start) ptr to end
val of 0
val of 1
...
val of n
(end) null ptr

到目前为止,这看起来像一个数组..让我们删除一个值:

(start) ptr to end
val of 0
val of 1
...
(deleted0) val of m (= null)
...
val of n
(end) ptr to deleted0

现在插入一个值x

(start) ptr to end
val of 0
val of 1
...
val of m (= x)
...
val of n
(end) null ptr

我现在看到的一个问题只是在我输入时,如果我们需要插入没有删除,或者甚至必须保留多个指向删除的指针,这些指针会延伸到“end以下”,我们如果它存在,则不允许覆盖内存中的其他项目。

那么在那个例子中,为什么不表现得像链接列表,整个结构作为“节点”?删除2项:

(start) ptr to end
val of 0
val of 1
...
val of m (= x)
...
(deleted0) val of a (= null)
...
(deleted1) val of b (= null)
...
val of n
(end) ptr to start1
...
...
(start1) ptr to end1
(end1) ptr to deleted0
ptr to deleted1

好的,假设我们现在因删除而有一些空白但没有要插入的内容。也许我们想要稍微清理一下,通过填充start1start2结构来“缩小”是相当简单的。start(0)中有空格我想我们会有别无选择,只能将其复制出来。

请注意,end不是'end',而是值的结尾,我们在其中寻找指向内部空闲空间的指针结构

  • 与保存我们的链接列表相比,这与数组相比具有相同的优势 从插入/删除复制。
  • 内部的'自由'空间没有被释放到其他变量,但是我们可以认为这是结构的成本,可比(但在最差的情况下,除了链表之外的所有情况都要小得多)
  • 插入所需的指令与链接列表中的指令一样少。
  • 删除的次数少于ll。
  • 内存中的大小大于数组的大小,但小于链接列表*。
  • 从不完全复制,对于大n,所需的最大空间往往为n,并且与num_insertions/num_deletions成比例

*'最坏的情况'是每个其他元素都被删除的时候,我们基本上都有一个链接列表,其中包含所有指针。

在开始愤怒地输入之前,你可能没有兴趣阅读这篇文章:

  

那么我的问题是什么?!

简单:这个想法出了什么问题(除非当然使用它/非常相似的东西,在这种情况下 - 它叫什么?) - 是否只是我们想要插入/删除的速度总是很清楚或者为了节省内存中的通常空间,这是多余的?

谢谢,对于长篇文章感到抱歉,我觉得需要几个例子才能明确。

1 个答案:

答案 0 :(得分:1)

首先,请记住我们有不同的结构,因为我们有不同的要求。您的数据结构可能非常适合某些应用程序,但它肯定不适合作为链接列表的通用替代。

以下是三个数据结构无法替换链接列表的示例:

插入中间

您专注于删除,这很容易,因为您只是将该地点标记为已删除(这种方式并非总是可行。您的数据结构可能不一定存储指针,因此您可以使用NULL作为标记)。 / p>

但是,如果阵列已满,您需要在中间插入什么?然后,您将被迫移动数组的所有内容(向左或向右移动到最近的孔,如果已满,则移动到数组的末尾),这非常昂贵。

充当FIFO

如果将结构用作队列,请考虑结构会发生什么。您将在最后连续插入(即使用realloc放大数组)并从开头标记为已删除。实际上,你的数组的大小会严格增加,即注定要失败。

共享节点

链表的一个好处是,无论实际链接如何变化,数据都在同一个内存位置持久存在。想象一下,有人引用了列表中节点的数据(即指向节点数据的指针)。如果将这些数据存储在数组中,那么任何数据移位(插入时不可避免)都会使这些指针无效。但是,对于链接列表,无论您在链接列表中插入或删除的内容和位置,数据地址都保持不变(除非您删除该特定节点!)