如何根据它们的值获取std :: map的前n个键?

时间:2013-07-31 07:13:56

标签: c++ c++11

如何根据价值获得std :: map的前n个键? 有没有办法让我可以得到一个例如前10个键的列表,其价值最大值为什么? 假设我们有一个类似于此的地图:

mymap["key1"]= 10;
mymap["key2"]= 3;
mymap["key3"]= 230;
mymap["key4"]= 15;
mymap["key5"]= 1;
mymap["key6"]= 66;
mymap["key7"]= 10; 

我只想要一个前十个键列表,它们与另一个键相比具有更大的价值。 例如,mymap的前4位是

key3
key6
key4 
key1
key10 

注意:
值不是唯一的,实际上它们是每个键的出现次数。我想获得大多数发生的密钥列表

注2:
如果地图不是一个好的候选人,你想提出任何建议,请按照c ++ 11进行,我当时不能使用提升。

注3:
如果使用std::unordered_multimap<int,wstring>我还有其他选择吗?

7 个答案:

答案 0 :(得分:20)

map的顺序是基于其键而不是其值,并且不能重新排序,因此有必要迭代map并维护前十个遇到的列表或作为评论通过Potatoswatter使用partial_sort_copy()为您提取最高 N 值:

std::vector<std::pair<std::string, int>> top_four(4);
std::partial_sort_copy(mymap.begin(),
                       mymap.end(),
                       top_four.begin(),
                       top_four.end(),
                       [](std::pair<const std::string, int> const& l,
                          std::pair<const std::string, int> const& r)
                       {
                           return l.second > r.second;
                       });

请参阅online demo

选择不同类型的容器可能更合适,boost::multi_index值得研究,其中:

  

...允许构造容器,维护一个或多个具有不同排序和访问语义的索引。

答案 1 :(得分:3)

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;

int main(int argc, const char * argv[])
{
    map<string, int> entries;

    // insert some random entries
    for(int i = 0; i < 100; ++i)
    {
        string name(5, 'A' + (char)(rand() % (int)('Z' - 'A') ));
        int number = rand() % 100;

        entries.insert(pair<string, int>(name, number));
    }

    // create container for top 10
    vector<pair<string, int>> sorted(10);

    // sort and copy with reversed compare function using second value of std::pair
    partial_sort_copy(entries.begin(), entries.end(),
                      sorted.begin(), sorted.end(),
                      [](const pair<string, int> &a, const pair<string, int> &b)
    {
        return !(a.second < b.second);
    });

    cout << endl << "all elements" << endl;

    for(pair<string, int> p : entries)
    {
        cout << p.first << "  " << p.second << endl;
    }

    cout << endl << "top 10" << endl;

    for(pair<string, int> p : sorted)
    {
        cout << p.first << "  " << p.second << endl;
    }

    return 0;
}

答案 2 :(得分:1)

std::map不仅没有按映射值排序(这些值不需要任何已定义的排序顺序),它不允许重新排列其元素,因此对假设执行++ map[ "key1" ];将值映射回键的结构将使向后映射无效。

最好的办法是将键值对放入另一个结构中,并在需要向后映射时按值进行排序。如果您始终需要向后映射,则每次更改值时都必须删除,修改和重新添加。

将现有地图分类为新结构的最有效方法是std::partial_sort_copy,正如Al Bundy所说明的那样(刚才)。

答案 3 :(得分:1)

由于映射值未编入索引,因此您必须阅读所有内容并选择10个最大值。

std::vector<mapped_type> v;
v.reserve(mymap.size());

for(const auto& Pair : mymap)
 v.push_back( Pair.second );

std::sort(v.begin(), v.end(), std::greater<mapped_type>());

for(std::size_t i = 0, n = std::min<int>(10,v.size()); i < n; ++i)
  std::cout << v[i] << ' ';
另一种方法是使用两个映射或一个bimap,因此映射的值将被排序。

答案 4 :(得分:1)

您正在寻找的算法是nth_element,它会对范围进行部分排序,以便第n个元素处于完全排序范围内。例如,如果您希望按降序排列前三项,则需要编写(在伪C ++中)

nth_element(begin, begin + 3, end, predicate)

问题是nth_element不能与std :: map一起使用。因此,我建议您将数据结构更改为对的向量(根据您正在处理的数据量,您可能会发现这是一个更快的数据结构)。所以,就你的例子而言,我会这样写:

typedef vector<pair<string, int>> MyVector;
typedef MyVector::value_type ValueType;

MyVector v; 

// You should use an initialization list here if your
// compiler supports it (mine doesn't...)
v.emplace_back(ValueType("key1", 10));
v.emplace_back(ValueType("key2", 3));
v.emplace_back(ValueType("key3", 230));
v.emplace_back(ValueType("key4", 15));
v.emplace_back(ValueType("key5", 1));
v.emplace_back(ValueType("key6", 66));
v.emplace_back(ValueType("key7", 10));

nth_element(v.begin(), v.begin() + 3, v.end(), 
    [](ValueType const& x, ValueType const& y) -> bool
    {
        // sort descending by value
        return y.second < x.second;
    });

// print out the top three elements
for (size_t i = 0; i < 3; ++i)
    cout << v[i].first << ": " << v[i].second << endl;

答案 5 :(得分:1)

#include "stdafx.h"
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <cassert>
#include <iterator>
using namespace std;

class MyMap
{
public:
    MyMap(){};
    void addValue(string key, int value)
    {
        _map[key] = value;
        _vec.push_back(make_pair(key, value));
        sort(_vec.begin(), _vec.end(), Cmp());
    }
    vector<pair<string, int> > getTop(int n)
    {
        int len = min((unsigned int)n, _vec.size());
        vector<Pair> res;
        copy(_vec.begin(), _vec.begin() + len, back_inserter(res));
        return res;
    }
private:
    typedef map<string, int> StrIntMap;
    typedef vector<pair<string, int> > PairVector;
    typedef pair<string, int> Pair;
    StrIntMap  _map;
    PairVector _vec;
    struct Cmp: 
        public binary_function<const Pair&, const Pair&, bool>
    {
        bool operator()(const Pair& left, const Pair& right)
        {
            return right.second < left.second;
        }
    };
};

int main()
{
    MyMap mymap;
    mymap.addValue("key1", 10);
    mymap.addValue("key2", 3);
    mymap.addValue("key3", 230);
    mymap.addValue("key4", 15);
    mymap.addValue("key6", 66);
    mymap.addValue("key7", 10);

    auto res = mymap.getTop(3);

    for_each(res.begin(), res.end(), [](const pair<string, int> value)
                                        {cout<<value.first<<" "<<value.second<<endl;});
}

答案 6 :(得分:1)

最简单的解决方案是使用std::transform来构建 第二张地图:

typedef std::map<int, std::string> SortedByValue;
SortedByValue map2;
std::transform(
    mymap.begin(), mymap.end(),
    std::inserter( map2, map2.end() ),
    []( std::pair<std::string, int> const& original ) {
        return std::pair<int, std::string>( original.second, original.first );
        } );

然后选择map2的最后n个元素。

或者(可能更有效率),您可以使用 std::vector<std::pair<int, std::string>>并对其进行排序 算账:

std::vector<std::pair<int, std::string>> map2( mymap.size() );
std::transform(
    mymap.begin(), mymap.end()
    map2.begin(),
    []( std::pair<std::string, int> const& original ) {
        return std::pair<int, std::string>( original.second, original.first );
        } );
std::sort( map2.begin(), map2.end() );

(请注意,这些解决方案会以时间为代价优化 更多的记忆。)