【问题标题】:Remove duplicate elements from std::vector but starting from the front?从 std::vector 中删除重复元素但从前面开始?
【发布时间】:2021-05-14 15:18:29
【问题描述】:

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

所以

2 3 4 5 2 5 将变为 3 4 2 5

1 5 3 1 将变为 5 3 1

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

【问题讨论】:

  • 您的意思是最容易编码,还是担心运行时效率?
  • @AdrianMole 两者都有,但更容易阅读是首选
  • 好的。 “最容易阅读”而不是“最容易写入”意味着某种程度的优雅。想...
  • 只需采用现有的算法之一,以通常的方式进行操作,然后反转所有内容:从头到尾迭代,而不是从头到尾迭代,然后删除剩余位在向量的开头,而不是向量的结尾,通过erase() 处理剩余的位。任务完成。
  • @TedLyngmo 看来您是基于意见关闭的。如果是这样,我已经稍微编辑了这个问题,只是一个“操作方法”,我认为现在可以打开了。

标签: c++ algorithm vector iterator erase-remove-idiom


【解决方案1】:

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

这是一个演示程序。

#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'; 
} 

程序输出是

1 2 3 4 5 4 3 2 1 
1 2 3 4 5 

1 2 3 4 5 4 3 2 1 
5 4 3 2 1 

【讨论】:

    【解决方案2】:

    一种渐近有效的算法(N log N):

    • 创建一系列迭代器,每个迭代器都指向原始容器
    • 使用 stable 排序对迭代器进行排序。使用间接通过迭代器的比较函数。
    • 使用反向迭代器和类似的自定义比较函数删除连续重复项 (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());
    

    【讨论】:

      猜你喜欢
      • 2011-11-13
      • 1970-01-01
      • 1970-01-01
      • 2010-10-26
      • 2020-12-26
      • 2016-03-30
      • 2011-07-20
      • 1970-01-01
      相关资源
      最近更新 更多