【问题标题】:How can I remove specific duplicates from a multimap?如何从多图中删除特定的重复项?
【发布时间】:2020-02-25 15:19:11
【问题描述】:

我有一个带有重复项(重复键)的多图,我想从中删除所有重复项,除了最近添加到多图的那个。

假设我有

multimap<int, int> multi;
multi.insert(pair<int, int>(1, 1));
multi.insert(pair<int, int>(1, 12));
multi.insert(pair<int, int>(2, 500));
multi.insert(pair<int, int>(2, 789));
multi.insert(pair<int, int>(1, 888));

我已经遍历了这张地图,它看起来像这样:

1 : 1
1 : 12
1 : 888
2 : 500
2 : 789

现在我希望删除重复的 1 : 11 : 12,因为最后添加的一对是 1 : 888。同时删除 2 : 500,因为 2 : 789 最近被添加到地图中。

但是我该怎么做呢?我试图将这个多图的每个元素放入法线贴图中,因为它会自行删除重复项。问题是它不会保留最近添加的副本:c

【问题讨论】:

  • 你为什么不使用std::map,然后用operator[]添加值?
  • 你看过equal_range吗?它将返回具有相同键的元素范围。然后,您应该删除除最后一个之外的所有内容。
  • 我敢打赌,如果使用插入,将多图复制到地图中将保留每个元素的第一个。但我不确定这是否得到保证。但如果是这种情况,使用反向迭代器进行复制可能只会保留最新的元素。编辑:在重复键的情况下,未指定使用哪个元素。有一个建议可以解决这个问题:cplusplus.github.io/LWG/issue2844
  • 您是想获得一个只有唯一键的新容器,还是想将 multi 就地转换为拥有唯一键?
  • @eyesima 听起来你并不需要复制。如果我理解正确,在插入之前,您可以先find 查看该键是否已存在,更新任何现有元素然后替换它。然后你可以使用map 而不必担心重复。

标签: c++ algorithm dictionary c++11 data-structures


【解决方案1】:

您可以改用std::map,因为它是:

... 一个已排序的关联容器,其中包含键值对 唯一键。

std::map::insert 不起作用,因为它:

... 将元素插入容器,如果容器没有 已包含具有等效键的元素。

但是,您可以改用operator[] 来解决此问题,因为它不会检查密钥是否已存在。

通过执行以下操作,地图中只会保留最后一个值:

#include <map>
#include <iostream>

int main() {
    std::map<int, int> my;
    my[1] = 1;
    my[1] = 12;
    my[2] = 500;
    my[2] = 789;
    my[1] = 888;
    return 0;
}

现在,std::map 看起来像:

1 888
2 789

更新

如果您能够使用 C++17,则可以使用 std::map::insert_or_assign,它与 C++17 之前的 operator[] 做同样的事情。

你可以像这样使用它:

std::map<int, int> my;
my.insert_or_assign(1, 1);
my.insert_or_assign(1, 12);
my.insert_or_assign(2, 500);
my.insert_or_assign(2, 789);
my.insert_or_assign(1, 888);

结果是一样的。

【讨论】:

  • insert 不起作用,但是从 C++17 开始就有insert_or_assign
【解决方案2】:

您可以通过以下方式进行操作

#include <iostream>
#include <map>
#include <iterator>

int main() 
{
    std::multimap<int, int> multi;
    multi.insert( { 1, 1 } );
    multi.insert( { 1, 12 } );
    multi.insert( { 2, 500 } );
    multi.insert( { 2, 789 });
    multi.insert( { 1, 888 } );

    for ( const auto &item : multi )
    {
        std::cout << "{ "  << item.first << ", " << item.second << " } ";
    }

    std::cout << '\n';

    if ( !multi.empty() )
    {
        for ( auto prev = std::begin( multi ), 
                   next = std::begin( multi ),  
                   last = std::end( multi );  
              ++next != last; )
        {
            if ( next->first == prev->first ) prev = multi.erase( prev );
            else ( ++prev );
        }             
    }

    for ( const auto &item : multi )
    {
        std::cout << "{ "  << item.first << ", " << item.second << " } ";
    }

    std::cout << '\n';

    return 0;
}

程序输出是

{ 1, 1 } { 1, 12 } { 1, 888 } { 2, 500 } { 2, 789 } 
{ 1, 888 } { 2, 789 } 

如果您想创建具有唯一键的副本,则可以使用标准算法std::unique_copy。例如

#include <iostream>
#include <map>
#include <iterator>
#include <algorithm>

int main() 
{
    std::multimap<int, int> multi;
    multi.insert( { 1, 1 } );
    multi.insert( { 1, 12 } );
    multi.insert( { 2, 500 } );
    multi.insert( { 2, 789 });
    multi.insert( { 1, 888 } );

    for ( const auto &item : multi )
    {
        std::cout << "{ "  << item.first << ", " << item.second << " } ";
    }

    std::cout << '\n';
    std::multimap<int, int> multi2;

    std::unique_copy( std::rbegin( multi ), std::rend( multi ),
                      std::inserter( multi2, std::end( multi2 ) ),
                      []( const auto &a, const auto &b ) 
                      { 
                         return a.first == b.first ; 
                      } );

    for ( const auto &item : multi2 )
    {
        std::cout << "{ "  << item.first << ", " << item.second << " } ";
    }

    std::cout << '\n';

    return 0;
}

程序输出同上图

{ 1, 1 } { 1, 12 } { 1, 888 } { 2, 500 } { 2, 789 } 
{ 1, 888 } { 2, 789 } 

【讨论】:

  • 哎呀我的错误。 ++next 增加了迭代器,所以没问题。
猜你喜欢
  • 2021-11-27
  • 1970-01-01
  • 2018-05-20
  • 2020-01-30
  • 2019-02-05
  • 1970-01-01
  • 1970-01-01
  • 2022-01-19
  • 1970-01-01
相关资源
最近更新 更多