如果像这样的动态数据结构是可能的,为什么我们需要一个链表?

时间:2014-03-28 01:52:10

标签: c++ arrays dynamic data-structures linked-list

如果我们std::setstd::vector可以动态增长和缩小,为什么我们需要链接列表?

N.B。我真的不明白,为什么会有这么多的下选票。 沮丧的选民,请留下评论。

5 个答案:

答案 0 :(得分:13)

  

为什么我们需要链表?

链接列表是O(1)插入,删除,连接,等等。列表变大了?那么,系统保持正常运行。只是计划不需要随机访问。

向量在(可能)二进制块中增长,因此时间命中增长到相当大且相当快的时间。这是一个不可多得的时间。

在旧版Dell的以下输出中,在Ubuntu-15.04上使用g ++ 4.9.2,最后一列是自上次容量更改以来的毫秒时间。 (最后一栏匆匆添加,为您重复使用该示例;)

     element         vector     bytes stack use         bytes heap use
      count       capacity    'sizeof(vector)'     (element count * element size)
          0              0                24                       0         0
          1              1                24                       4         0
          2              2                24                       8         0
          3              4                24                      12         0
          5              8                24                      20         0
          9             16                24                      36         0
         17             32                24                      68         0
         33             64                24                     132         0
         65            128                24                     260         1
        129            256                24                     516         0
        257            512                24                   1,028         0
        513          1,024                24                   2,052         0
      1,025          2,048                24                   4,100         0
      2,049          4,096                24                   8,196         0
      4,097          8,192                24                  16,388         1
      8,193         16,384                24                  32,772         2
     16,385         32,768                24                  65,540         3
     32,769         65,536                24                 131,076         8
     65,537        131,072                24                 262,148        12
    131,073        262,144                24                 524,292        28
    262,145        524,288                24               1,048,580        50
    524,289      1,048,576                24               2,097,156        38
  1,048,577      2,097,152                24               4,194,308        76
  2,097,153      4,194,304                24               8,388,612       149
  4,194,305      8,388,608                24              16,777,220       300
  8,388,609     16,777,216                24              33,554,436       601
 16,777,217     33,554,432                24              67,108,868      1215
 33,554,433     67,108,864                24             134,217,732      2475
 67,108,865    134,217,728                24             268,435,460      4982
134,217,729    268,435,456                24             536,870,916     10247
268,435,457    536,870,912                24           1,073,741,828     21387

这有足够的理由吗?我不确定。

我使用矢量作为我的列表(几乎从未增长到技嘉大小,我在找到最大尺寸时添加了保留的使用),对于我的fifo(处理文件和目录创建了数百万字符串) ),并且通常忽略该列表,因为我如何使用矢量足够快(哎呀,不能相信我只是大声说出来)。

此表中的最后一个条目是向量容量达到512 K元素(从512 K - 1)。分配,数据复制和擦除(使用小对象的noop dtors)但没有更多的push_back()s,导致堆栈上有1 G字节,耗时21秒。

(我想我不再希望在这台旧机器上安装8 Gig。)

编辑 - 2015年7月1日

我重新使用此代码重新生成表格的最后两行并捕获挂钟信息。

134,217,729    268,435,456                24             536,870,916      9769
268,435,457    536,870,912                24           1,073,741,828     20983

real 0m41.147s
user 0m39.928s
sys  0m1.164s

挂钟时间报告41秒。

生成最后一条输出线需要21秒(以实时时钟为单位,以毫秒为单位),

生成前29行的20秒......不仅仅是指数!

编辑 - 2015年7月3日 - 认为OP需要关于set vs linked-list的一些评论。

  

如果我们有std :: set [和...]可以动态增长和缩小,为什么呢   我们需要链表吗?

来自cppreference.com:

  

std :: set是一个关联容器,包含一组有序的   Key类型的唯一对象。使用密钥比较进行排序   功能比较。搜索,删除和插入操作具有   对数复杂度。集合通常实现为红黑色   树。

一方面,一个集合(我很少使用)有点像链表,因为会有节点(当实现为红黑树时),并且每个节点都是独立分配的。当集合变大时,系统保持相对正常运行(始终如一?)(至少在节点进入交换之前(我说交换,而不是沼泽)。

恕我直言,一套(rbtree)没有'工作积压'。相反,树在每次插入/追加/删除工作上进行平衡。 (我只能用一点小小的努力来取笑这样的证据......对不起,没有桌子可看。)

当您查看红黑树时,您会发现rbtree不包含数据,只包含键(和颜色)。

我可以想象一个只包含数据中键和颜色的链表(如集合)。软件非常灵活。但是我的想象力让我失望了。

我非常欣赏关联容器的想法。我发现std :: map非常有用,它影响了我对几种软件挑战的看法。

但是,我还没有调查过地图是如何完成其​​魔力的。更多要学习。

答案 1 :(得分:7)

Besides the constant insert/remove/splice time, linked lists provide another useful property - iterators are not invalidated when the list is altered (except of course iterators that point to deleted elements. Thus, if you have a linked list, you can keep an iterator around as a pointer into the list.

Though you can get a similar effect with a vector by storing the index of an element, using a list and storing an iterator has the advantage that, no matter what happens to the list, as long as the element still exists, that iterator will always point to the same element. If you used a vector, you'd need to fix up the index if an element earlier in the vector was removed.

答案 2 :(得分:5)

虽然目前给出的所有答案都是正确的,但我试着用适合C ++初学者的文字来解释它。

std :: vector(或一般数组)的优点是你可以在相对较短的时间内通过索引访问它的任何元素。然而,缺点之一是在靠近开始中间的位置插入元件是相对昂贵的。在这种情况下,需要分配更多存储空间,并且必须将插入的存储器之后的所有元素复制到新位置。在最坏的情况下,无法分配额外空间,必须将整个阵列复制到新位置。

相反,访问列表的第1000个元素相对较慢,因为我们必须遵循999链接到列表的下一个元素。但是,在该位置插入新元素相对容易且快速。我们只需要为新元素分配空间并修改几个指针。

你知道,矢量有其特定的优势,列表也是如此。

答案 3 :(得分:5)

因为在某些特定情况下我们有时需要O(1)插入/擦除性能。我们无法通过setvector实现目标。

+========+=========+=========+=========+===============+
|        | Insert  | Erase   | Lookup  | Iterator      |
+========+=========+=========+=========+===============+
| vector | O(N)    | O(N)    | O(1)    | Random Access |
+--------+---------+---------+---------+---------------+
| set    | O(logN) | O(logN) | O(logN) | Sequential    |
+--------+---------+---------+---------+---------------+
| list   | O(1)    | O(1)    | O(N)    | Sequential    |
+--------+---------+---------+---------+---------------+

答案 4 :(得分:4)

它既不是 - 它是一个用长度注释的固定大小的数组,以避免缓冲区溢出。

它确实有办法调整它的大小,但是Resize方法破坏性地擦除了内部数组,这有点奇怪。它应该分配新的目标,复制成员,然后解除分配原始数组。