C++ 链表的用途是否有限?

C++ 链表的用途是否有限?,c++,language-agnostic,container-data-type,C++,Language Agnostic,Container Data Type,今天我仔细看了一下我的STL选项。然后我想到了一件事 链表(std::list)似乎用途有限。也就是说,它只是看起来 如果有用 容器中元素的顺序很重要,并且 >p>我需要删除或插入元素在中间。 也就是说,如果我只需要大量数据,而不关心数据的顺序,如果我想要O(logn)查找,最好使用std::set(平衡树),如果我想要O(1)预期查找,最好使用std::unordered_映射(散列映射),如果我想要更好的引用位置,最好使用std::vector(连续数组),或者使用std::deque

今天我仔细看了一下我的STL选项。然后我想到了一件事

链表(std::list)似乎用途有限。也就是说,它只是看起来 如果有用

  • 容器中元素的顺序很重要,并且

  • >p>我需要删除或插入元素在中间。

也就是说,如果我只需要大量数据,而不关心数据的顺序,如果我想要O(logn)查找,最好使用std::set(平衡树),如果我想要O(1)预期查找,最好使用std::unordered_映射(散列映射),如果我想要更好的引用位置,最好使用std::vector(连续数组),或者使用std::deque(双端队列)如果我需要在前面和后面插入

OTOH,如果顺序确实重要,我最好使用std::vector来获得更好的引用局部性和更少的开销,或者如果需要进行大量调整,最好使用std::deque


那么,我是不是遗漏了什么?还是说链表就没那么好?除了中间插入/擦除之外,究竟为什么有人要使用一个呢?

顺序通常很重要。如果是这样,链表就很好了。如果您有一个不断增长的集合,那么您可以选择链表、数组列表(
vector
,在C++中)和双端队列(
deque
)。如果要经常修改(添加、删除)列表中任何位置的元素,请使用链接列表。如果快速检索很重要,请使用数组列表。如果要向数据结构的两端添加内容,则使用双端队列,快速检索非常重要。对于
deque
vs
vector
问题:使用
vector
,除非从一开始就插入/删除内容很重要,在这种情况下,请使用
deque
。请参阅以深入了解这一点


如果顺序不重要,链表通常不是理想的。

任何类型的插入/删除都是O(1)。即使std::vector对于appends来说不是O(1),它也接近O(1),因为大多数时候是这样,但有时您必须增加该数组


它还非常擅长处理批量插入、删除。如果您有100万条记录,并且希望从另一个列表(concat)中追加100万条记录,则为O(1)。每一个其他结构(假设stadard/NAVIE实现)至少是O(n)(其中n是添加的元素数)

这个问题让我想起了。阅读本书,了解为何如此简单的数据结构如此重要的相似之处。

链表是一种基本的数据结构。其他数据结构,如哈希映射,可能在内部使用链表。

为了便于查找,两种不同的算法可能具有O(1)时间复杂度,但这并不意味着它们具有相同的性能。例如,第一个可能比第二个快10倍或100倍。


每当您需要存储、迭代和处理大量数据时,该任务的正常(和快速)数据结构就是链表。更复杂的数据结构适用于特殊情况,ie Set适用于不需要重复值的情况。

std::list具有以下属性:

  • 序列
  • 前消音
  • 背升力
  • 远期集装箱
  • 反向容器
在这些属性中,std::vector没有(反向seuchence)
而std::set不支持任何序列属性或(反向容器)

那么这意味着什么呢?
返回序列是否支持rend()和rbegin()等的O(1)

有关完整信息,请参阅:

链表是不可变的递归数据结构,而数组是可变的和必需的(=基于更改)。在函数式编程中,通常没有数组——您不更改元素,而是将列表转换为新列表。虽然链表在这里甚至不需要额外的内存,但这在数组中是不可能实现的

您可以轻松构建或分解列表,而无需更改任何值

double [] = []
double (head:rest) = (2 * head):(double rest)

在C++中,这是一种命令式语言,你不会经常使用<代码>列表< /C> > s。一个例子可能是游戏中的宇宙飞船列表,您可以从中轻松移除自上一帧以来被摧毁的所有宇宙飞船。

std::list
以其
splice()
方法著称,该方法允许您在固定时间内将多个元素从一个列表移动到另一个列表,不复制或分配任何元素或列表节点。

由于数组的增长,向量的附加值为O(n)。然而,为了避免混淆,它被摊销为O(1):如果你愿意在你要追加的100万条记录和新的合并列表之间没有独立性,那么连接只是O(1)。O(1)操作—将最后一个原始列表元素的“下一个”指针指向100万条记录中的第一个元素—导致这样一种情况,即以后对原始100万条记录所做的任何更改也会反映在组合列表中。要使新列表独立,需要复制所有元素,这是一个O(n)操作。@Talljoe,我想你答案中的第二段是关于splice()方法的?@bk1e,我是用一般术语说的,但我认为splice就是这么做的。您可以优化向量(不一定是std:vector),通过使用不相交数组以更昂贵的检索为代价,将拼接作为摊销O(1)(将其视为单个插入)进行处理。在这一点上,它不再成为一个真正的向量,并且是一个完全不同的结构。如果你有一个指向要删除或插入之前或之后的节点的指针,则链表插入/删除仅为O(1)。如果该节点不是头或尾,那么您必须以某种方式搜索它,并且该搜索通常应包含在插入/删除时间中。std::list支持两端添加(IIRC,std::dequeue本质上是std::list)。deque通常以块的形式分配,而列表具有节点。因此,deque执行的al更少