移除元素的算法

Posted

技术标签:

【中文标题】移除元素的算法【英文标题】:Algorithm for removing elements 【发布时间】:2017-01-27 10:06:30 【问题描述】:

我知道 C++ 有擦除删除习语。而<algorithm> 下的remove 方法会将目标元素移动到范围的后面。

但是,下面的输出让我感到困惑。

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main() 
    vector<int> vec = 10, 20, 30, 20, 30, 20, 10, 10, 20;

    auto pend = remove(vec.begin(), vec.end(), 20);
    cout << "After removing 20: " << endl;
    for (const auto& x : vec) 
        cout << x << " ";
    
    cout << endl;

    cout << "use pend: " << endl;
    for (auto p = vec.begin(); p != pend; p++) 
        cout << " " << *p;
    

    cout << endl;
    return 0;

输出是:

After removing 20:
10 30 30 10 10 20 10 10 20
use pend:
10 30 30 10 10

这里有两个问题:

    对于“去掉20后”,为什么后面有10和20混在一起? 10 30 30 10 10 20 10 10 20

    对于“使用挂起:”,为什么它无法打印最后两个 10?原向量中有5个10,10是不是应该去掉?

从库中,remove() 方法返回迭代器 pend

模板 ForwardIterator remove (ForwardIterator first, ForwardIterator last, const T& val); 指向未删除的最后一个元素之后的元素的迭代器。 first 和这个迭代器之间的范围包括序列中所有不等于 val 的元素。

【问题讨论】:

1.删除是通过将比较等于 val 的元素替换为下一个不等于返回的迭代器中的元素和输入相关的后缀的元素来完成的。 2.它打印三个 10 因为你的输入包含三个。 这并不能解决问题,但不要使用std::endl,除非您需要它所做的额外内容。 '\n' 结束一行。 它在原始数组中包含五个 10 【参考方案1】:

从数组中:

10 20 30 20 30 20 10 10 20

当您删除所有20 时,您期望得到:

10 30 30 10 10

std::remove 只是移动项目,它让剩余的值unspecified:

指向新逻辑端和范围物理端之间元素的迭代器仍然是可解引用的,但元素本身具有未指定的值

所以你得到:

10 30 30 10 10 xx xx xx xx
               ^
               pend

这解释了你的结果。

如果您需要擦除项目,请致电vec.erase(pend, vec.end())

调用 remove 之后通常会调用容器的擦除方法,该方法会擦除未指定的值并减小容器的物理大小以匹配其新的逻辑大小。

【讨论】:

谢谢。但是原始向量包含五个 10,而删除 20 实际上会留下最后两个 10。这是为什么呢? 没有。在您的代码中,原始向量包含 3 个10 对。 remove() 方法的输出中的五个 10 包含未指定的值,即两个额外的 10。【参考方案2】:

在 C++ 中,这称为erase-remove idiom。从***页面:

erase-remove 习惯用法是一种常见的 C++ 技术,用于从 C++ 标准库容器中删除满足特定条件的元素。

另请参阅 Scott Meyers 有效的 C++ 书籍。

【讨论】:

以上是关于移除元素的算法的主要内容,如果未能解决你的问题,请参考以下文章

Java算法 每日一题 编号27:移除元素

算法leetcode|27. 移除元素(rust重拳出击)

算法leetcode|27. 移除元素(rust重拳出击)

算法leetcode|27. 移除元素(rust重拳出击)

算法移除数组中指定元素

算法移除数组中指定元素