找到向量中所有具有相同值的元素,然后将它们全部从向量中删除
Find all elements with the same value in a vector, then erase all of them out of the vector
问题是我需要在向量中找到所有具有相同值的元素,对它们做一些处理,然后从向量中删除所有元素。继续这样做,直到向量为空。
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 循环有问题,但我不知道是什么。任何帮助将不胜感激。
问题出在您的 erase
电话上。 erase
不会自动更新迭代器;它 returns 指向下一个元素的迭代器。要修复它,您需要使用
it2 = L.erase(it2);
以后
it = L.erase(it);
如果目标是
- 处理重复然后
- 擦除它们
有一个更简单的解决方案,那就是使用 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 << "\n\nHere is the resulting vector:\n";
copy(L.begin(), L.end(), ostream_iterator<int>(cout, " "));
}
这是相同的代码,但使用 C++ 03(对于没有 C++11 / 14 的代码):
注意上面的代码中没有循环。一切都由算法函数完成。分区、平均计算、擦除等,都是在没有循环的情况下执行的。
基本上我们测试每个项目以查看该项目是否存在于集合中。如果是,那么它将转到分区的右侧,如果不是,则该项目将转到分区的左侧。
return 值 divider
是分区中 "dividing line" 元素的迭代器。一旦项目被分区,我们就可以通过使用 divider
告诉我们 "good" 项目在哪里以及 "about to be erased" 项目在哪里来轻松处理它们。
顺便说一句,这在我第一次成功编译它时就起作用了——"luck" 让它快速运行的一个主要原因是算法在给定正确的参数(和如果谓词函数编写正确)。在一个容器中,尤其是vector这样的序列容器中,擦除、移动等项,几乎都是用1、2、3个算法函数来覆盖。
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();
这是我的解决方案,其灵感来自 PaulMcKenzie 的代码(请不要介意我重复使用了您的某些代码行)。就像他一样,我没有使用任何手写循环,而是使用了STL的算法,这是人们应该首先考虑的。
在我的解决方案中,我只使用了另一个完全相同类型的容器和两个简单的算法:std::remove_if
和 std::find
。这样代码使意图更清晰一点:找到重复项并删除它们。另请注意 std::move
的使用,当容器包含比 int
更复杂的东西时,它应该派上用场。在那种情况下,当然可以考虑使用 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 << "\n\nHere is the resulting vector:\n";
std::copy(L.begin(), L.end(), std::ostream_iterator<vt>(std::cout, " "));
return 0;
}
查看实际效果 here。
我终于找到了我自己问题的解决方案。问题是
- 我们有一个包含许多重复元素的向量
L
。
- 我们需要找到所有的 dup 元素
- 对 dup 元素做点什么
我的方法是在 L
上使用 2 个迭代器,比如 it
和 it2
。 it
保留在 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 函数的使用。您的代码水平很高,初学者很难。无论如何,我选择它作为您贡献的最佳答案。我真的从你那里学到了很多东西。感谢您的快速回复。
问题是我需要在向量中找到所有具有相同值的元素,对它们做一些处理,然后从向量中删除所有元素。继续这样做,直到向量为空。
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 循环有问题,但我不知道是什么。任何帮助将不胜感激。
问题出在您的 erase
电话上。 erase
不会自动更新迭代器;它 returns 指向下一个元素的迭代器。要修复它,您需要使用
it2 = L.erase(it2);
以后
it = L.erase(it);
如果目标是
- 处理重复然后
- 擦除它们
有一个更简单的解决方案,那就是使用 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 << "\n\nHere is the resulting vector:\n";
copy(L.begin(), L.end(), ostream_iterator<int>(cout, " "));
}
这是相同的代码,但使用 C++ 03(对于没有 C++11 / 14 的代码):
注意上面的代码中没有循环。一切都由算法函数完成。分区、平均计算、擦除等,都是在没有循环的情况下执行的。
基本上我们测试每个项目以查看该项目是否存在于集合中。如果是,那么它将转到分区的右侧,如果不是,则该项目将转到分区的左侧。
return 值 divider
是分区中 "dividing line" 元素的迭代器。一旦项目被分区,我们就可以通过使用 divider
告诉我们 "good" 项目在哪里以及 "about to be erased" 项目在哪里来轻松处理它们。
顺便说一句,这在我第一次成功编译它时就起作用了——"luck" 让它快速运行的一个主要原因是算法在给定正确的参数(和如果谓词函数编写正确)。在一个容器中,尤其是vector这样的序列容器中,擦除、移动等项,几乎都是用1、2、3个算法函数来覆盖。
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();
这是我的解决方案,其灵感来自 PaulMcKenzie 的代码(请不要介意我重复使用了您的某些代码行)。就像他一样,我没有使用任何手写循环,而是使用了STL的算法,这是人们应该首先考虑的。
在我的解决方案中,我只使用了另一个完全相同类型的容器和两个简单的算法:std::remove_if
和 std::find
。这样代码使意图更清晰一点:找到重复项并删除它们。另请注意 std::move
的使用,当容器包含比 int
更复杂的东西时,它应该派上用场。在那种情况下,当然可以考虑使用 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 << "\n\nHere is the resulting vector:\n";
std::copy(L.begin(), L.end(), std::ostream_iterator<vt>(std::cout, " "));
return 0;
}
查看实际效果 here。
我终于找到了我自己问题的解决方案。问题是
- 我们有一个包含许多重复元素的向量
L
。 - 我们需要找到所有的 dup 元素
- 对 dup 元素做点什么
我的方法是在 L
上使用 2 个迭代器,比如 it
和 it2
。 it
保留在 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 函数的使用。您的代码水平很高,初学者很难。无论如何,我选择它作为您贡献的最佳答案。我真的从你那里学到了很多东西。感谢您的快速回复。