【问题标题】:erase duplicate elements keeping order [duplicate]删除重复元素保持顺序[重复]
【发布时间】:2020-12-18 11:48:53
【问题描述】:

我想从向量中删除重复元素,同时保持向量的当前顺序。

下面我有一个建议的实现。首先,这样安全吗?

其次,从“使用 C++ 算法而不是重新发明轮子”的角度来看,是否有更好的方法可以更有效或更好地做到这一点。

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

int main()
{
    using namespace std;

    std::vector<int> v= {1, 7, 2, 3, 8, 4, 5, 3, 2, 3, 2, 6, 2, 3, 2, 9, 10, 1, 2, 2, 1};
    std::vector<int>::iterator finalEnd = v.end();
    for (auto vIter = v.begin(); vIter != v.end(); ++vIter) {
        for (auto nextvIter = vIter + 1; nextvIter != v.end(); ++nextProjIter) {
            if (*vIter == *nextvIter)
                finalEnd = std::remove(vIter, finalEnd, *nextvIter);
        }
    }
    v.erase(finalEnd, v.end());

    for(auto p : v)
        cout << p << "  ";

    //Should return:  1  7  2  3  8  4  5  6  9  10

    return EXIT_SUCCESS;
}

【问题讨论】:

  • 不,恐怕显示的逻辑存在致命缺陷,并且在某些极端情况下会失败。而且,是的,要回答被问到的问题,是的,有一种更好的方法可以做到这一点,使用 set 或 unordered_set 来跟踪重复项。只使用一个循环,应该需要大约一半的代码,并且要简单得多。有关使用集合和其他关联容器的更多信息,请参阅 C++ 教科书。
  • 尽可能使用 std::set。元素是有序的,并且不可能重复(至少对于从头开始的 POD 类型)。

标签: c++ vector erase


【解决方案1】:

使用std::unordered_set 跟踪重复项和std::stable_partition 将重复项与单独的值分开,同时保留项目的顺序,这是可以实现这一点的多种方法之一:

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

int main()
{
    std::unordered_set<int> numSet;
    std::vector<int> v= {1, 7, 2, 3, 8, 4, 5, 3, 2, 3, 2, 6, 2, 3, 2, 9, 10, 1, 2, 2, 1};
    auto iter = std::stable_partition(v.begin(), v.end(), [&](int n) 
           { bool ret = !numSet.count(n); numSet.insert(n); return ret; }); // returns true if the item has not been "seen"
    v.erase(iter, v.end());           
    for(auto p : v)
        std::cout << p << "  ";
}

输出:

1  7  2  3  8  4  5  6  9  10 

如果没有看到该项目,std::stable_partition 将返回true,因此将其放置在分区点的左侧。完成后,将返回一个指向分区点的迭代器,我们使用该迭代器从该点到向量末尾进行一次擦除。请注意,lambda 函数会为每个处理的项目更新 unordered_set

之所以用std::stable_partition代替std::remove_if是因为std::remove_if不能保证按顺序处理项目。例如,实现可以先处理该数据中的第二个1,而不是第一个1。所以为了安全起见stable_partition 不会擦除元素,而只是将元素放置在正确的位置,为最后的擦除做好准备。

【讨论】:

  • 保罗,这很好用并且回答了这个问题。还有一件事:为了简化问题,我使用 int 作为原始问题中的数据类型。事实上,我的数据是 boost::filesystem::path 对象。当我在路径对象的向量上使用上述函数时,我在 MSVC 2019 中遇到错误,特别是 'std::hash<_kty>::hash(const std::hash<_kty> &)':尝试引用已删除的功能。我知道这不是原始帖子的一部分,但如果您对此有任何建议,我将不胜感激。
  • 问题可能是 std::unordered_set 的对象要求它们具有必要的哈希函数(您需要将其作为第二个模板参数提供给 unordered_set。因为您使用的是 boost ,有boost::hash。也许在完整路径名上创建一个散列是为文件系统路径设计这样一个散列的一种方法。
【解决方案2】:

通过构造一个新向量,您可以将此向量初始化为不重复的。您可以为此使用查找功能。我建议你搜索std :: find

std::vector<int> v= {1, 7, 2, 3, 8, 4, 5, 3, 2, 3, 2, 6, 2, 3, 2, 9, 10, 1, 2, 2, 1};
std::vector<int> nonDuplicateVect;

for (int element : v)
    if(std::find(nonDuplicateVect.begin(), nonDuplicateVect.end(), element) == nonDuplicateVect.end())
        nonDuplicateVect.push_back(element);

for (int element : nonDuplicateVect)
    std::cout << element << " ";

std::cout << "\n";

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-21
    • 1970-01-01
    • 2016-12-28
    • 1970-01-01
    • 1970-01-01
    • 2013-07-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多