如何按值对STL地图进行排序?

时间:2010-04-23 13:50:38

标签: c++ algorithm sorting dictionary stl

如何按值实现STL地图排序?

例如,我有一张地图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

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

10 个答案:

答案 0 :(得分:61)

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

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

答案 1 :(得分:29)

您可以构建第二个地图,第一个地图的值作为键,第一个地图的键作为值。

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

答案 2 :(得分:16)

  

我想知道如何按值实现STL地图排序。

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

答案 3 :(得分:4)

你应该使用Boost.Bimap来做这类事情。

答案 4 :(得分:1)

我刚刚在我的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;
}

答案 5 :(得分:1)

根据@ swegi的想法,我使用multimapc++11中实施了一个解决方案:

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&#39;的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&#39;关于使用一组对的想法。但是,如果价值观并非完全不同,它就不会起作用。使用仅比较对的第二个元素的仿函数并不起作用。如果您首先将make_pair(1, 1)插入集合中,然后尝试插入make_pair(2, 1),则不会插入第二对,因为这两个对被视为相同。你可以看到效果here on Ideone

答案 6 :(得分:0)

创建另一个映射,根据值not key提供less()函数,如果value1 &lt; = value2(不严格&lt;),则函数应返回true。在这种情况下,也可以对具有非不同值的元素进行排序。

答案 7 :(得分:0)

我在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;
}

答案 8 :(得分:-1)

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

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);

答案 9 :(得分:-1)

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

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

using namespace std;

// 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;
    }
}


int main()
{

    map<int, int> m;

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

    customSort(m);
    
    return 0;
}