C++ 从std::vector中删除重复元素,但从前面开始?

C++ 从std::vector中删除重复元素,但从前面开始?,c++,algorithm,vector,iterator,erase-remove-idiom,C++,Algorithm,Vector,Iterator,Erase Remove Idiom,如何从向量中删除重复元素,但从前面开始 所以 2 3 4 5 2 5将变成3 4 2 5 1531将变成531 我希望该解决方案易于阅读,如果它具有良好的性能就更好了。如果容器支持双向迭代器,那么您尝试从容器的哪一侧删除重复元素并不重要,因为您可以使用反向迭代器 这是一个演示程序 #include <iostream> #include <vector> #include <iterator> #include <algorithm>

如何从向量中删除重复元素,但从前面开始

所以

2 3 4 5 2 5
将变成
3 4 2 5

1531
将变成
531


我希望该解决方案易于阅读,如果它具有良好的性能就更好了。

如果容器支持双向迭代器,那么您尝试从容器的哪一侧删除重复元素并不重要,因为您可以使用反向迭代器

这是一个演示程序

#include <iostream> 
#include <vector> 
#include <iterator> 
#include <algorithm> 
  
template <typename ForwardIterator> 
ForwardIterator remove_duplicates( ForwardIterator first, ForwardIterator last ) 
{ 
    for ( ; first != last; ++first ) 
    { 
        last = std::remove( std::next( first ), last, *first ); 
    } 
      
    return last; 
} 
  
int main() 
{ 
    std::vector<int> v = { 1, 2, 3, 4, 5, 4, 3, 2, 1 }; 
      
    for ( const auto &item : v ) std::cout << item << ' '; 
    std::cout << '\n'; 
  
    v.erase( remove_duplicates( std::begin( v ), std::end( v ) ), std::end( v ) ); 
      
    for ( const auto &item : v ) std::cout << item << ' '; 
    std::cout << '\n'; 
  
    std::cout << '\n'; 
      
    v.assign( { 1, 2, 3, 4, 5, 4, 3, 2, 1 } ); 
  
    for ( const auto &item : v ) std::cout << item << ' '; 
    std::cout << '\n'; 
      
    v.erase( std::begin( v ), remove_duplicates( std::rbegin( v ), std::rend( v ) ).base() ); 
     
    for ( const auto &item : v ) std::cout << item << ' '; 
    std::cout << '\n'; 
} 

渐近有效算法(N logn):

  • 创建一系列迭代器,每个迭代器都指向原始容器
  • 使用稳定排序对迭代器进行排序。使用通过迭代器间接执行的比较函数
  • 使用反向迭代器和类似的自定义比较函数删除连续的重复项(
    std::unique
  • std::remove_if
    使用谓词函数从向量中删除迭代器不在辅助容器中的元素
它比O(N*N)解复杂一点。下面是一个示例实现。我不能保证它是正确的:

template<class It, class Sentinel>
auto remove_duplicates(It first, Sentinel last)
{
    std::vector<It> iterators;

    auto iterator_generator = [it = first]() mutable {
        return it++;
    };
    std::generate_n(
        std::back_inserter(iterators),
        last - first,
        iterator_generator);

    auto iterator_compare = [](const auto& l, const auto& r) {
        return *l < *r;
    };
    
    std::stable_sort(
        iterators.begin(),
        iterators.end(),
        iterator_compare);

    auto iterator_eq = [](const auto& l, const auto& r) {
        return *l == *r;
    };
    auto last_unique = std::unique(
        iterators.begin(),
        iterators.end(),
        iterator_eq);
    iterators.erase(last_unique, iterators.end());

    auto keep_generator = [it = first]() mutable {
        return it++;
    };
    std::vector<bool> remove(last - first, true);
    for(auto it : iterators) {
        auto index = it - first;
        remove[index] = false;
    }
    
    auto remove_predicate = [index = 0, remove = std::move(remove)](const auto& el) mutable {
        return remove[index++];
    };
    return std::remove_if(first, last, std::move(remove_predicate));
}

// usage with reverse iterators
v.erase(
    v.rend().base(),
    remove_duplicates(v.rbegin(), v.rend()).base());
模板
自动删除重复项(先删除,后删除)
{
向量迭代器;
自动迭代器_生成器=[it=first]()可变{
返回它++;
};
std::生成(
std::back_插入器(迭代器),
最后,首先,,
迭代器(U发生器);
自动迭代器_compare=[](常量自动&l,常量自动&r){
返回*l<*r;
};
std::稳定排序(
迭代器。begin(),
迭代器.end(),
迭代器(比较);
自动迭代器_eq=[](常量自动&l,常量自动&r){
返回*l==*r;
};
自动上次_unique=std::unique(
迭代器。begin(),
迭代器.end(),
迭代器(eq);
erase(last_unique,iterators.end());
自动保持_生成器=[it=first]()可变{
返回它++;
};
std::vector remove(last-first,true);
for(自动it:迭代器){
自动索引=it-第一;
删除[索引]=false;
}
auto remove_谓词=[index=0,remove=std::move(remove)](const auto&el)可变{
返回删除[index++];
};
返回std::remove_if(第一个、最后一个std::move(remove_谓词));
}
//使用反向迭代器
v、 抹去(
v、 rend().base(),
删除重复项(v.rbegin(),v.rend()).base();

你是说最容易编写代码,还是担心运行时的效率?@AdrianMole两者都有,但最容易阅读的是preferredOK。“易读”而不是“易写”意味着某种程度的优雅。思考……只需采用一种现有的算法,以通常的方式进行操作,并将所有操作都反转:从结尾到开头,而不是从开头到结尾进行迭代,然后通过
擦除
()删除剩余位,移除向量开头的剩余位,而不是向量结尾。任务完成了。@TedLyngmo看来你是以意见为基础的。如果是这样的话,我已经把这个问题稍加修改,变成了一个“如何做”,我想现在可以公开了。
template<class It, class Sentinel>
auto remove_duplicates(It first, Sentinel last)
{
    std::vector<It> iterators;

    auto iterator_generator = [it = first]() mutable {
        return it++;
    };
    std::generate_n(
        std::back_inserter(iterators),
        last - first,
        iterator_generator);

    auto iterator_compare = [](const auto& l, const auto& r) {
        return *l < *r;
    };
    
    std::stable_sort(
        iterators.begin(),
        iterators.end(),
        iterator_compare);

    auto iterator_eq = [](const auto& l, const auto& r) {
        return *l == *r;
    };
    auto last_unique = std::unique(
        iterators.begin(),
        iterators.end(),
        iterator_eq);
    iterators.erase(last_unique, iterators.end());

    auto keep_generator = [it = first]() mutable {
        return it++;
    };
    std::vector<bool> remove(last - first, true);
    for(auto it : iterators) {
        auto index = it - first;
        remove[index] = false;
    }
    
    auto remove_predicate = [index = 0, remove = std::move(remove)](const auto& el) mutable {
        return remove[index++];
    };
    return std::remove_if(first, last, std::move(remove_predicate));
}

// usage with reverse iterators
v.erase(
    v.rend().base(),
    remove_duplicates(v.rbegin(), v.rend()).base());