Remove duplicates from a list<int>

喜欢而已 提交于 2019-12-28 04:20:09

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!