如何按值对 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<pair<K, V> >
中,其中set
是用一个仅比较该对的第二个值的小于函子构造的。这样,即使您的值并非完全不同,您的代码仍然有效。
或者将键值对转储到vector<pair<K, V> >
,然后使用相同的小于函子对该向量进行排序。
【讨论】:
一个问题,你现在不会使用双内存吗? @AtoMerZ 如果这是个问题,您可以让集合保持引用。如果它们已经是引用或小类型,那没关系。 对向量进行排序:***.com/questions/279854/… 将地图“转储”到矢量:vector<pair<K,V>> 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<multiset_of<int>, set_of<double> >
【参考方案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 <bits/stdc++.h>
。另外,最好不要推荐using namespace std;
。见:Why should I not #include <bits/stdc++.h>?以上是关于如何按值对 STL 映射进行排序?的主要内容,如果未能解决你的问题,请参考以下文章