问题
Using STL algorithms (as much as possible) such as remove_if()
and list::erase
, is there a nice way to remove duplicates from a list defined as following:
list<int> l;
Please note that list::unique()
only works if duplication occurs in consecutive elements. In my case, all duplicates have to be eliminated regardless of their position in the list. Moreover, removing duplicates mean preserving only one copy of each element in the final result.
EDIT: The option to l.sort()
followed by l.unique()
cannot be availed as that will destroy the order of the list.
回答1:
Using the list::remove_if
member function, a temporary hashed set, and lambda expression.
std::list<int> l;
std::unordered_set<int> s;
l.remove_if([&](int n) {
return (s.find(n) == s.end()) ? (s.insert(n), false) : true;
});
回答2:
If preserving the order of the list is not important, you can just do list.sort(); list.unique();
If the order is important, use Rup's suggestion:
list<int>::iterator iter = l.begin();
set<int> elements;
while (iter != l.end()) {
if (elements.find(*iter) != elements.end())
iter = l.erase(iter);
else {
elements.insert(*iter);
++iter;
}
}
回答3:
He said he wanted to use the erase-remove idiom, so here's a possible way, using a function object:
struct Unifier{
set<int> foundElements;
bool operator()(int & a){
if(foundElements.find(a) != foundElements.end()){
return true;
}else{
foundElements.insert(a);
return false;
}
}
};
int main(){
list<int> v;
v.push_back(5);
v.push_back(4);
v.push_back(5);
v.push_back(3);
v.push_back(5);
v.push_back(3);
copy (v.begin(), v.end(), ostream_iterator<int>(cout," "));
Unifier u;
v.remove_if(u);
cout << endl << "After:" << endl;
copy (v.begin(), v.end(), ostream_iterator<int>(cout," "));
}
Update: The above code has a subtle bug. According to C++11 [algorithms.general]/10
:
[Note: Unless otherwise specified, algorithms that take function objects as arguments are permitted to copy those function objects freely. Programmers for whom object identity is important should consider using a wrapper class that points to a noncopied implementation object such as
reference_wrapper<T>
(20.8.3), or some equivalent solution. —end note ]
There appears to be no "otherwise specified" for std::list::remove_if
, so this code may fail to remove all duplicates because it may create copies of the predicate at the start, and then use different copies of the predicate for different parts of the list. Example of this actually happening for std::remove_if.
A simple fix for C++11 is to replace v.remove_if(u)
with:
v.remove_if( reference_wrapper<decltype(u)>(u) );
In C++03 I'm not sure if the above quote was present; but if it was then a fix would be to make foundElements
be static, or to refactor Unifier
so that all copies of it reference a single instance of foundElements
.
Link to related question
来源:https://stackoverflow.com/questions/4885676/remove-duplicates-from-a-listint