查找矢量中具有相同值的所有元素,然后从矢量中擦除所有元素

Find all elements with the same value in a vector, then erase all of them out of the vector

本文关键字:元素 擦除 然后 查找      更新时间:2023-10-16

问题是我需要在向量中找到所有具有相同值的元素,对它们执行某些操作,然后将它们全部从向量中删除。继续这样做,直到向量为空。

vector<unsigned> L;
vector<unsigned>::iterator it, it2, it3;
vector<unsigned> vec;
unsigned Z;
// populate the vector (1, 2, 3, 4, 2, 4)
for(unsigned i = 1; i <= 4; ++i)
   L.push_back(i);
for(unsigned i = 2; i <= 4; i = i + 2)
   L.push_back(i);
it = L.begin();
while(it != L.end() -1){
  cout<< "*it = " << *it << endl;
  Z=0;
  vec.clear();
  it2 = it + 1;
  cout<< "*it2 = " << *it2 << endl;
  while(it2 != L.end()){
     cout << "Loop *it2 = " << *it2 <<endl;
     if(*it == *it2){
        vec.push_back(*it);
        L.erase(it2);  // iterator automatically points to the next element
        cout<< "after erase(it2), *it2 = " << *it2 << endl;
        continue;
     }
     ++it2;
  }
// do something (here I calculate the average)
  for(it3 = vec.begin(); it3 != vec.end(); ++it3)
     Z = Z+ *it3;
  Z= Z/vec.size();
  cout<< "Z = " << Z << endl << endl;
  L.erase(it); // iterator automatically points to the next element
}

输出为:

*it = 1
*it2 = 2
Loop *it2 = 2
Loop *it2 = 3
Loop *it2 = 4
Loop *it2 = 2
Loop *it2 = 4

然后它停止工作

如果我用这段代码填充向量

// populate the vector (1, 2, 3, 4, 1, 3)
for(unsigned i = 1; i <= 4; ++i)
   L.push_back(i);
for(unsigned i = 1; i <= 4; i = i + 2)
   L.push_back(i);

然后输出为

*it = 1
*it2 = 2
Loop *it2 = 2
Loop *it2 = 3
Loop *it2 = 4
Loop *it2 = 1
after erase(it2), *it2 = 3
Loop *it2 = 3
Z = 1
*it = 2
*it2 = 3
Loop *it2 = 3
Loop *it2 = 4
Loop *it2 = 3

它在这里停止工作

我知道第二个while循环有问题,但我无法弄清楚它是什么。任何帮助将不胜感激。

如果目标是

  1. 处理重复项,然后
  2. 擦除它们
有一个

更简单的解决方案,那就是使用 std::stable_partition 以及一个std::set

#include <algorithm>
#include <vector>
#include <iterator>
#include <set>
#include <iostream>
#include <numeric>
using namespace std;
int main()
{
    std::vector<int> L = { 1, 2, 4, 3, 2, 4 };
    std::set<int> tempSet;
    //...
    // partition the elements, unique items on left, duplicates on right
    auto divider = stable_partition(L.begin(), L.end(), [&](int n)
    {
        // return true if item is not in the set, false otherwise
        return tempSet.insert(n).second;
    });
    // do something with the duplicates, for example, print them
    cout << "Here are the dups:n";
    copy(divider, L.end(), ostream_iterator<int>(cout, " "));
    // get the average
    // get number of duplicates  
    size_t numDups = std::distance(divider, L.end());  
    double avg = 0.0;
    // compute average using std::accumulate
    if ( numDups > 0 ) 
       avg = std::accumulate(divider, L.end(), 0.0) / numDups;
    cout << "nThe average of the duplicates is: " << avg << "n";
    // erase the duplicates
    L.erase(divider, L.end());
    // print the updated vector now
    cout << "nnHere is the resulting vector:n";
    copy(L.begin(), L.end(), ostream_iterator<int>(cout, " "));
}

现场示例,C++ 14

这是相同的代码,但使用 C++ 03(对于那些没有 C++11/14 的人(:

现场示例,C++ 03

请注意,上面的代码中没有循环。 一切都由算法函数完成。 分区、平均计算、擦除等都是在没有循环的情况下执行的。

基本上,我们测试每个项目以查看该项目是否存在于集合中。 如果是这样,那么它将转到分区的右侧,如果没有,则该项目将转到分区的左侧。

返回值divider是分区中作为"分界线"的元素的迭代器。 一旦项目被分区,那么我们就可以通过使用divider告诉我们"好"项目在哪里以及"即将删除"项目在哪里来轻松处理它们。

顺便说一句,这是我第一次成功编译它时就起作用了——这种"运气"让它快速工作的一个主要原因是,当给定正确的参数时(并且如果谓词函数编写正确(,算法函数只是普通工作。 擦除容器中的项目、移动项目等,尤其是像矢量这样的序列容器,几乎总是使用 1、2 或 3 个算法函数来涵盖。

这是我

的解决方案,它的灵感来自PaulMcKenzie的代码(请不要介意我重用你的一些台词(。就像他一样,我没有使用任何手写循环,而是使用了STL的算法,人们应该始终首先考虑这一点。

在我的解决方案中,我只使用另一个完全相同类型的容器,并且只有两种简单的算法:std::remove_ifstd::find。这样,代码使意图更加清晰:查找重复项并删除它们。另请注意,当拥有比int更复杂的容器时,使用std::move应该派上用场。在这种情况下,人们可能不得不考虑使用std::find_if当然。

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main()
{
    std::vector<int> L = { 1, 2, 4, 3, 2, 4 };
    using cont = decltype(L);
    using vt = cont::value_type;
    // find duplicates and move them to separate container
    cont D;
    D.reserve(L.size());
    D.swap(L);
    D.erase(std::remove_if(D.begin(), D.end(), [&L] (vt& value)
    {
        if (L.cend() == std::find(L.cbegin(), L.cend(), value))
        {
            L.emplace_back(std::move(value));
            return true;
        }
        return false;
    }), D.end());
    // do something with the duplicates, for example, print them
    std::cout << "Here are the dups:n";
    std::copy(D.cbegin(), D.cend(), std::ostream_iterator<vt>(std::cout, " "));
    // print the vector now
    std::cout << "nnHere is the resulting vector:n";
    std::copy(L.begin(), L.end(), std::ostream_iterator<vt>(std::cout, " "));
    return 0;
}

在这里看到它的实际效果。

问题出在您的erase呼叫上。 erase不会自动更新迭代器;它将迭代器返回到下一个元素。 要修复它,您需要使用

it2 = L.erase(it2);

及以后

it = L.erase(it);
vector<unsigned> L;
vector<unsigned>::iterator it, it2, it3;
vector<unsigned> vec;
unsigned Z;
// populate the vector (1, 2, 3, 4, 2, 4)
for(unsigned i = 1; i <= 4; ++i)
    L.push_back(i);
for(unsigned i = 2; i <= 4; i = i + 2)
    L.push_back(i);
std::set<unsigned int> myset;
for(it = L.begin(); it != L.end(); ++it){
    myset.insert(*it);
}
for(std::set<unsigned int>::iterator iter = myset.begin();
    iter != myset.end(); ++iter){
    std::cout << "element = " << *iter << std::endl;
    Z += *iter;
}
std::cout << "sum = " << Z << std::endl;
Z = Z/myset.size();
std::cout<< "Average value = " << Z << std::endl;
L.clear();

我终于找到了我自己问题的解决方案。问题是

  1. 我们有一个包含许多重复元素的向量L
  2. 我们需要找到所有的 dup 元素
  3. 对 dup 元素执行某些操作

我的方法是在L上使用 2 个迭代器,比如说 itit2it保留在L的开头,而it2将穿过向量。如果*it*it2具有相同的值,那么我们将*it2放入临时向量vec中。然后,我们L.erase(it2). it2会一直移动到L年底,将所有dup元素收集到vec中,然后从L中删除它们。当it2到达L末尾时,我们称之为L.erase(it)。之后,该过程继续,直到向量L为空。

这是我修改的代码。需要注意的最重要的事情是 while中的状况 .我使用 while(it < L.end()) 而不是 while(it != L.end())因为当我们擦除元素时,迭代器会以某种方式将条件传递!= .

我正在使用旧的编译器(早于 C++0x(,因为我现在不想弄乱环境。因此,任何人都可以安全地使用我的代码。

vector<unsigned> L;
vector<unsigned>::iterator it, it2, it3, it4;
vector<unsigned> vec; //temporary variable to store matched elements
float Z;
// populate the vector (1, 2, 3, 4, 1, 3)
for(unsigned i = 1; i <= 4; ++i)
  L.push_back(i);
for(unsigned i = 1; i <= 4; i = i + 2)
  L.push_back(i);

it = L.begin();
while(it < L.end()){
  cout<< "*it = " << *it << endl;
  Z=0;
  vec.clear();
  vec.push_back(*it);
  if(L.size() == 1){
     cout << "Last element of vector = " << *it <<endl;
     goto Loop1;
  }
  else
     it2 = it + 1;
  while(it2 < L.end()){
     cout << "Loop *it2 = " << *it2 <<endl;   //debug
     if(*it == *it2){
        vec.push_back(*it2);
        L.erase(it2);  // iterator automatically points to the next element
        cout<< "after erase(it2), *it2 = " << *it2 << endl;   //debug
        continue;
     }
     ++it2;
  }
  Loop1:
  for(it3 = vec.begin(); it3 != vec.end(); ++it3)
     Z = Z+ *it3;
  Z= Z/vec.size();
  cout<< "Z = " << Z << endl ;
  if(L.empty()) break;
  L.erase(it); // iterator automatically points to the next element
  //debug
  cout<< endl <<  "new vector = "; 
  for(it4 = L.begin(); it4 != L.end(); ++it4) 
     cout << *it4 << ' ';
  cout << endl;
}

@PaulMcKenzie:我花了一段时间才了解您在 C++14 中的写作风格以及我不熟悉的 STL 函数的使用。您的代码处于非常高的水平,对于初学者来说很难。无论如何,我选择它作为您贡献的最佳答案。我真的从你身上学到了很多东西。感谢您的快速回复。