【问题标题】:List vs Vector, remove an element while looping throughList vs Vector,在循环中删除一个元素
【发布时间】:2012-07-22 09:40:23
【问题描述】:

我需要一个容器,让我可以在循环遍历它时快速擦除 ONE 元素。我不需要直接访问,因为我总是在循环中访问它。

在这种情况下,List 是否比 Vector 快?

伪代码:

vector<Item*> myContainer;

for(..loop over it...) {

  if (someCondition)
    myContainer.erase(currentElement)
}

删除一个元素后,我需要继续循环其余元素

【问题讨论】:

    标签: c++ performance list vector


    【解决方案1】:

    是的,理论上列表比向量提供更快的删除,但实际上,没有人使用列表。为什么不?因为向量产生的堆活动少得多,而且它们提供了更好的缓存局部性。

    您确定绝对需要循环遍历向量并逐个擦除元素吗?这将导致向量的二次复杂度,但erase-remove-idiom 在线性时间内完成:

    #include <algorithm>
    
    myContainer.erase(
        std::remove_if(
            myContainer.begin(),
            myContainer.end(),
            [](Item* p) {
                return p->someCondition;
            }
        ),
        myContainer.end()
    );
    

    (如果向量拥有项目,您应该将其替换为vector&lt;unique_ptr&lt;Item&gt;&gt;。)

    作为替代方案,如果您确实必须逐个删除元素,并且您不关心 Items 的相对顺序,则可以使用neat little trick 在恒定时间内删除向量的一个元素。

    【讨论】:

    • @yes123 当然,只需将您想要对每个项目执行的操作放在 lambda 中,在 return p-&gt;someCondition; 行之前。有一个变异谓词有点不寻常,但我不明白为什么它不应该在这种特定情况下工作。或者,首先执行for_each 来更新项目,然后应用擦除/删除习语来删除不需要的项目。那会更干净一些,但也可能对缓存不太友好。
    【解决方案2】:

    使用列表擦除任意位置的元素会更快:

    Complexity of std::vector::erase:与删除的元素数量(析构函数)加上最后一个元素删除(移动)之后的元素数量成线性关系。

    Complexity of std::list::erase:与擦除的元素数量(析构函数)成线性关系。

    另外,擦除后迭代器会失效,所以你需要更新你的迭代器:

    it = list.erase(it);
    

    【讨论】:

    • 这也取决于向量的长度。移动已经在 CPU 缓存中的一堆指针 (Item*) 可以与释放列表节点一样快(这也需要重新链接)。并不是说 O(1)总是比 O(n) 快。
    • @yes123 - 对,我说的是“实践中”。使用向量通常快得惊人,因为它的元素在内存中是相邻的并且最终在同一个缓存行中。指针链表也将使用 3 倍于指针向量的内存。我只是说移动一个大盒子 (O(1)) 并不总是 比 n 个小盒子 (O(n)) 好。
    【解决方案3】:

    C++ 列表是一个链表。所以元素将按如下方式排列,

     head <--> 1 <--> 2 <--> 3 <--> 4 <--> 5
    

    所以,当我想删除 3 时,它只是一个指针改组。我想擦除 3,我需要移动 2 的指针指向 4。在这种情况下,我需要移动到 3(定位元素)。时间复杂度将是 O(1) 最坏的情况,因为它是一个双向链表,并且您已经确定了要删除的元素。

    但另一方面,c++ 向量只是一个数组。因此,从数组中删除元素之后是向量元素删除。在这种情况下,您可以在 O(1) 复杂度中发现要擦除的元素。但是要删除它,您需要左移所有元素以删除创建的空白。所以复杂度是 O(n) 最坏的情况。口头上的复杂性可能类似于“n - 要删除的元素的索引”。

    列表产生 O(1) 和向量 O(n) 渐近的删除复杂度。但是该列表将有额外的开销来维护每个元素的指针(总共 O(2n) 空间)。

    【讨论】:

    • 嗯,既然你已经在迭代,list 会更快。抱歉,兄弟错过了那部分。
    • 继续强调我的观点(对不起:-)。在这个例子中,对象本身也是指针。因此,对于链表,您必须更改两个指针(在24),然后处理包含3 的节点。使用向量,您只需复制两个指针(45),然后就完成了。哪种操作最快?
    • @BoPersson 假设列表最多包含 20 个元素。什么会更快?列出,不是吗?
    • @MohanKumar - 我仍然会押注向量。我们仍在谈论复制 10 或 20 个指针的纳秒。如果是一百万个条目,可能会有所不同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-12
    相关资源
    最近更新 更多