如何按值对 STL 映射进行排序?

Posted

技术标签:

【中文标题】如何按值对 STL 映射进行排序?【英文标题】:How can I sort an STL map by value? 【发布时间】:2011-02-11 13:06:54 【问题描述】:

如何实现 STL map 按值排序?

比如我有一张地图m

map<int, int> m;
m[1] = 10;
m[2] = 5;
m[4] = 6;
m[6] = 1;

我想按m 的值对该地图进行排序。所以,如果我打印地图,我想得到如下结果:

m[6] = 1
m[2] = 5
m[4] = 6
m[1] = 10

如何以这种方式对地图进行排序?有什么方法可以用排序值处理键和值?

【问题讨论】:

boost::bimap 有一个类似的Java question。 通过比较器的概念使用自定义排序函数 【参考方案1】:

首先将所有键值对转储到set&lt;pair&lt;K, V&gt; &gt; 中,其中set 是用一个仅比较该对的第二个值的小于函子构造的。这样,即使您的值并非完全不同,您的代码仍然有效。

或者将键值对转储到vector&lt;pair&lt;K, V&gt; &gt;,然后使用相同的小于函子对该向量进行排序。

【讨论】:

一个问题,你现在不会使用双内存吗? @AtoMerZ 如果这是个问题,您可以让集合保持引用。如果它们已经是引用或小类型,那没关系。 对向量进行排序:***.com/questions/279854/… 将地图“转储”到矢量:vector&lt;pair&lt;K,V&gt;&gt; v(m.begin(), m.end());***.com/questions/684475/… 恕我直言,如果值不完全不同,set 的想法将不起作用。如果您执行myset.insert(make_pair(1, 1));myset.insert(make_pair(2, 1)),则如果函子仅比较这对的第二个值,则不会插入第二对,因为对于该集合,这两个项目是相同的。【参考方案2】:

您可以构建第二个映射,将第一个映射的值作为键,将第一个映射的键作为值。

仅当所有值都不同时才有效。如果您不能假设这一点,那么您需要构建多地图而不是地图。

【讨论】:

如果所有的值都是唯一的,那没关系。如何处理具有相同值的多个键?所以这个解决方案不好! 这甚至不是一个解决方案。如果值都相同怎么办?那么你的第二张地图将只有 1 个元素!【参考方案3】:

我想知道如何实现 STL 映射按值排序。

根据定义,你不能。地图是一种按键对其元素进行排序的数据结构。

【讨论】:

如何实现这个功能?我需要这个功能。 @Charlie Epps:还有另一张地图?每次向第一个映射添加一个键/值时,都会向第二个映射添加一个值/键...【参考方案4】:

你应该使用Boost.Bimap来处理这种事情。

【讨论】:

...前提是您的映射是一对一的。 其实Boost.Bimap支持非一对一的操作。例如,bimap&lt;multiset_of&lt;int&gt;, set_of&lt;double&gt; &gt;【参考方案5】:

基于@swegi 的想法,我在c++11 中使用multimap 实现了一个解决方案:

map<int, int> m = 1, 10, 2, 5, 4, 6, 6, 1;
multimap<int, int> mm;

for(auto const &kv : m)
    mm.insert(make_pair(kv.second, kv.first));  // Flip the pairs.

for(auto const &kv : mm)
    cout << "m[" << kv.second << "] = " << kv.first << endl;  // Flip the pairs again.

Code on Ideone

我还使用成对向量实现了基于@Chris 想法的 C++11 解决方案。为了正确排序,我提供了一个lambda expression 作为比较函子:

map<int, int> m = 1, 10, 2, 5, 4, 6, 6, 1;
using mypair = pair<int, int>;

vector<mypair> v(begin(m), end(m));

sort(begin(v), end(v), [](const mypair& a, const mypair& b)  return a.second < b.second; );

for(auto const &p : v)
    cout << "m[" << p.first << "] = " << p.second << endl;

Code on Ideone

第一个解决方案更紧凑,但两个解决方案的性能应该大致相同。插入multimap 的时间为 O(log n),但对于 n 条目必须这样做,导致 O(n log n)。在第二个解决方案中对向量进行排序也会导致 O(n log n)

我还尝试了@Chris 关于使用一组配对的想法。但是,如果这些值不是全部不同,它将不起作用。使用仅比较该对的第二个元素的仿函数没有帮助。如果您首先将make_pair(1, 1) 插入到集合中,然后尝试插入make_pair(2, 1),则不会插入第二对,因为该集合认为这两对是相同的。你可以看到那个效果here on Ideone。

【讨论】:

第二种解决方案还有一个优点,它可以推广到对地图值的非平凡数据类型进行排序【参考方案6】:

我刚刚在我的 c++ 书中做了一个类似的问题。我想出的答案可能不是很有效:

int main()

    string s;
    map<string, int> counters;

    while(cin >> s)
        ++counters[s];

    //Get the largest and smallest values from map
    int beginPos = smallest_map_value(counters);
    int endPos = largest_map_value(counters);

    //Increment through smallest value to largest values found
    for(int i = beginPos; i <= endPos; ++i)
    
        //For each increment, go through the map...
        for(map<string, int>::const_iterator it = counters.begin(); it != counters.end(); ++it)
        
            //...and print out any pairs with matching values
            if(it->second == i)
            
                cout << it->first << "\t" << it->second << endl;
            
        
    
    return 0;


//Find the smallest value for a map<string, int>
int smallest_map_value(const map<string, int>& m)

    map<string, int>::const_iterator it = m.begin();
    int lowest = it->second;
    for(map<string, int>::const_iterator it = m.begin(); it != m.end(); ++it)
    
        if(it->second < lowest)
            lowest = it->second;
    
    return lowest;


//Find the largest value for a map<string, int>
int largest_map_value(const map<string, int>& m)

    map<string, int>::const_iterator it = m.begin();
    int highest = it->second;
    for(map<string, int>::const_iterator it = m.begin(); it != m.end(); ++it)
    
        if(it->second > highest)
            highest = it->second;
    
    return highest;

【讨论】:

【参考方案7】:

创建另一个映射,根据值而不是键提供一个 less() 函数,并且如果 value1 value2 (严格来说不是

【讨论】:

【参考方案8】:

我在thispointer 找到了这个。该示例按所有 int 值对 std::map 进行排序。

#include <map>
#include <set>
#include <algorithm>
#include <functional>

int main() 

    // Creating & Initializing a map of String & Ints
    std::map<std::string, int> mapOfWordCount =   "aaa", 10 ,  "ddd", 41 ,
             "bbb", 62 ,  "ccc", 13  ;

    // Declaring the type of Predicate that accepts 2 pairs and return a bool
    typedef std::function<bool(std::pair<std::string, int>, std::pair<std::string, int>)> Comparator;

    // Defining a lambda function to compare two pairs. It will compare two pairs using second field
    Comparator compFunctor =
            [](std::pair<std::string, int> elem1 ,std::pair<std::string, int> elem2)
            
                return elem1.second < elem2.second;
            ;

    // Declaring a set that will store the pairs using above comparision logic
    std::set<std::pair<std::string, int>, Comparator> setOfWords(
            mapOfWordCount.begin(), mapOfWordCount.end(), compFunctor);

    // Iterate over a set using range base for loop
    // It will display the items in sorted order of values
    for (std::pair<std::string, int> element : setOfWords)
        std::cout << element.first << " :: " << element.second << std::endl;

    return 0;

【讨论】:

【参考方案9】:

最近不得不这样做。我最终使用了指针......

Quick Benchmark Results

#include <iostream>
#include <type_traits>
#include <algorithm>
#include <map>
#include <vector>

using map_t = std::map<int,int>;
const map_t m

     5, 20 ,
     -18, 28 ,
     24, 49 ,
     17, 27 ,
     23, 46 ,
     8, 16 ,
     -13, 11 ,
     -22, 32 ,
     12, 45 ,
     -2, 19 ,
     21, 11 ,
     -12, 25 ,
     -20, 8 ,
     0, 29 ,
     -5, 20 ,
     13, 26 ,
     1, 27 ,
     -14, 3 ,
     19, 47 ,
     -15, 17 ,
     16, 1 ,
     -17, 50 ,
     -6, 40 ,
     15, 24 ,
     9, 10 
;

template<typename T>
void sort_values_using_vector(T const& m)

    using map_t = T;

    using sort_t = std::vector<std::pair<typename map_t::key_type,
        typename map_t::mapped_type>>;
    sort_t sorted m.begin(), m.end() ;
    
    std::sort(sorted.begin(), sorted.end(),
        [](auto const& lhs, auto const& rhs)
        
            return lhs.second < rhs.second;
        );


template<typename T>
void sort_values_using_multimap(T const& m)

    using map_t = T;

    using sort_t = std::multimap<typename map_t::mapped_type,
        typename map_t::key_type>;
    sort_t sorted;

    for (auto const& kv : m)
    
        sorted.insert(std::make_pair(kv.second, kv.first));
    


template<typename T>
void sort_values_using_ptrs(T const& m)

    using map_t = T;

    using ptr_t = std::add_pointer_t
        <std::add_const_t<typename map_t::value_type>>;
    using sort_t = std::vector<ptr_t>;
    sort_t sorted;
    sorted.reserve(m.size());

    for (auto const& kv : m)
    
        sorted.push_back(std::addressof(kv));
    

    std::sort(sorted.begin(), sorted.end(),
        [](auto const& lhs, auto const& rhs)
        
            return lhs->second < rhs->second;
        );


template<typename T>
void sort_values_using_refs(T const& m)

    using map_t = T;

    using ref_t = std::reference_wrapper
        <std::add_const_t<typename map_t::value_type>>;
    using sort_t = std::vector<ref_t>;
    sort_t sorted m.begin(), m.end() ;

    std::sort(sorted.begin(), sorted.end(),
        [](auto const& lhs, auto const& rhs)
        
            return lhs.get().second < rhs.get().second;
        );


static void copy_to_vector(benchmark::State& state) 
  // Code inside this loop is measured repeatedly
  for (auto _ : state) 
    sort_values_using_vector(m);
  

BENCHMARK(copy_to_vector);

static void copy_flipped_to_multimap(benchmark::State& state) 
  // Code inside this loop is measured repeatedly
  for (auto _ : state) 
    sort_values_using_multimap(m);
  

BENCHMARK(copy_flipped_to_multimap);

static void copy_ptrs_to_vector(benchmark::State& state) 
  // Code inside this loop is measured repeatedly
  for (auto _ : state) 
    sort_values_using_ptrs(m);
  

BENCHMARK(copy_ptrs_to_vector);

static void use_refs_in_vector(benchmark::State& state) 
  // Code inside this loop is measured repeatedly
  for (auto _ : state) 
    sort_values_using_refs(m);
  

BENCHMARK(use_refs_in_vector);

【讨论】:

【参考方案10】:

此代码使用自定义排序函数按值对地图进行排序

// Comparator function to sort pairs
// according to value
bool comp(pair<int, int>& a,
        pair<int, int>& b)

    return a.second < b.second;


// Function to sort the map according
// to value in a (key-value) pair
void customSort(map<int, int>& m)


    vector<pair<int, int>> a;

    for(auto x:m)
        a.push_back(make_pair(x.first,x.second));

    sort(a.begin(), a.end(), comp);

    for (auto x:a) 
        cout << x.first<<" "<<x.second<<endl;
    

【讨论】:

从不建议在 Stack Overflow 上的回答中使用 #include &lt;bits/stdc++.h&gt;。另外,最好不要推荐using namespace std;。见:Why should I not #include <bits/stdc++.h>?

以上是关于如何按值对 STL 映射进行排序?的主要内容,如果未能解决你的问题,请参考以下文章

如何按值对数组进行排序(排序)? *有一个转折*

如何按值对 LevelDB 进行排序

如何按值对计数器进行排序? - Python

如何按值对 Python 字典进行排序? [复制]

如何按值对多维数组进行排序

在Java中按键或值对地图进行排序[重复]