向量比链表有啥优势
Posted
技术标签:
【中文标题】向量比链表有啥优势【英文标题】:What advantages do vectors have over linked lists向量比链表有什么优势 【发布时间】:2019-10-02 09:38:48 【问题描述】:我与我的教授进行了以下交流,这不是很令人满意。我包括了我的部分交流,这应该足以让我的观点得到理解。
“对于向量,C++实现是否遍历旧的动态分配数组的每个元素并释放它? (编辑:我的意思是,在调整大小和添加元素时,通过推回或调整大小)
我特别好奇,因为这本书试图证明链表很麻烦,因为每次都必须遍历。在我看来,向量在这方面并没有很大的优势。
我可以看到向量的主要好处是方便和快速访问,但仅此而已。例如,每次您尝试执行访问以外的操作时,您都将遍历所有内容以移动和释放内存。对吗?”
在他回复后,我补充道。
“xxxx教授,
我出去测试了,事实上,如果你调整大小或 push_back,地址就会改变,所以我假设旧地址被释放是正确的。我只能假设程序必须去每个元素来释放它,如果这是正确的,那么插入新事物会不会花费时间,甚至比遍历链表还要多?
如果以下陈述陈述了任何不正确的事实或假设,您能否更正一下。以除使用数组之外的任何其他方式使用向量(用于访问已存储数据之外的任何其他目的),意味着链表几乎总是更快,因为与链表不同,在向量中,您不仅会遍历元素,还会遍历它们,释放它们,然后创建一个全新的数组来容纳新空间。那是因为当前向量的最后一个元素之后的下一个地址可能有一个指针变量指向它,并且使用该地址会导致一种极其奇怪的行为,我无法想象可怜的灵魂试图找出问题所在的痛苦。”
TL;DR: 链表的缺点是遍历,但是向量的使用(push_back、resize() 等)通常都需要遍历,那么向量如何更快呢?
【问题讨论】:
将新事物插入vector
是摊销常数时间。有时会花费很多时间,但如果您插入 N
的东西,那么所花费的总时间将始终与 N
成正比。如果您必须每次(或大部分时间)“遍历”,那么它将与N²
成正比,但事实并非如此。
(当然是在向量的末尾插入)。
some benchmarks
vector
用于替换旧的array
的C
的线性数据结构,而linked list
用于非线性数据结构(即单链表或双链表)或树木等)。您将如何使用向量来表示有根的 b 树或在内存非常小的系统中管理数据的 I/O?如果您对大学项目或作业的要求只是将 POD 类型的数据仅存储在连续的内存块中,则可以使用您认为最合适或最喜欢的一个。
【参考方案1】:
有几件事比你预期的要快:
当向量重新分配时,原始元素被一个一个销毁,而不是被释放。然后立即释放它们的存储空间。这与链表相反,链表中的每个节点都是单独分配和释放的。但这有点没有实际意义,因为:
向量批量重新分配。 std::vector
是 specified 具有摊销的恒定插入成本,这意味着它避免了每次 push_back
时重新分配,在考虑复杂性时这个成本变得可以忽略不计的程度。每次超过向量容量时,典型的实现都会将向量的容量乘以一个固定因子,因此在执行代价高昂的重新分配时,它为接下来的几个push_back
s 提供了空间。然后这些就不需要遍历向量或分配任何东西。
向量对缓存非常友好。这使得向量上的所有顺序操作都非常快,并且在许多情况下可以反直觉地胜过链表,尤其是在内存可能碎片化的长时间运行的应用程序中。
【讨论】:
使该陈述更笼统。 要点是std::list
几乎从来没有更快。您在需要时使用(缺少)引用失效。
我仍然会在“摊销恒定时间”上竖起大拇指。这是一个非常重要的区别,也是比“通常不会每次都重新分配”更强有力的陈述——它会重新分配很少,以至于您通常不会注意到。
@MaxLanghof 我写的内容有意义吗?不要犹豫,提出修改建议:)
如果提前知道所需大小的信息,v.reserve(n);
可以完全避免重新分配。但是如果事先不知道大小,vector 可以很好地适应。 (如果向量的增长特性需要特殊处理——例如仅增长 100 个保留的额外点——reserve(n)
可用于增长比烘焙行为更受限制。)【参考方案2】:
作为已经给出的答案和 cmets 的补充......
std::vector
的元素连续存储在内存中,而链表则不是这样。
直接后果是元素访问对于 std::vector
来说是微不足道的,而对于链表来说却不是。
例如,如果我想访问链表的第 nth 个元素,我必须遍历该链表直到到达所需的元素。
但另一方面,如果我们想在里面插入一个新元素,链表会表现得更好。
实际上,对于链表,我们必须迭代直到到达所需位置,然后我们只需要更改前一个节点和下一个节点之间的连接,以便将新元素插入其中。
对于std::vector
,您必须在所需位置之后重新定位每个元素(并在需要时进行重新分配,即如果添加新元素超出保留的可用空间)。
所以std::vector
更适合元素访问,但在其中插入元素时效率较低(删除也是如此)。
【讨论】:
以上是关于向量比链表有啥优势的主要内容,如果未能解决你的问题,请参考以下文章