Как я могу сортировать карту 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

Как я могу сортировать карту таким образом? Есть ли способ, которым я могу справиться с ключом и значением с отсортированными значениями?

Ответ 1

Вы можете создать вторую карту с первыми значениями карты в качестве ключей и первых клавиш карты в качестве значений.

Это работает только в том случае, если все значения различны. Если вы не можете этого допустить, вам нужно создать мультимап вместо карты.

Ответ 2

Сначала выгрузите все пары ключ-значение в set<pair<K, V> >, где set построена с менее чем функтором, который сравнивает только пару второго значения. Таким образом, ваш код все еще работает, даже если ваши значения не все разные.

Или сбрасывать пары ключ-значение в vector<pair<K, V> >, а затем сортировать этот вектор с тем же менее чем функтором впоследствии.

Ответ 3

Интересно, как я могу реализовать сортировку по STL по значению.

Вы не можете, по определению. Карта - это структура данных, которая сортирует свой элемент по ключу.

Ответ 4

Вы должны использовать Boost.Bimap для такого рода вещей.

Ответ 5

Я только что сделал аналогичный вопрос в моей книге на С++. Ответ, который я придумал, может быть не очень эффективным:

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

Ответ 6

Основываясь на идее @swegi, я внедрил решение в С++ 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.

Код на Ideone

Я также реализовал решение С++ 11 на основе идеи @Chris, используя вектор пар. Для правильной сортировки я предоставляю 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;

Код на идеоне

Первое решение более компактно, но оба решения должны иметь примерно такую ​​же производительность. Вставка в multimap имеет значение O (log n), но это нужно сделать для n записей, в результате чего O (n log n). Сортировка вектора во втором решении также приводит к O (n log n).

Я также попробовал идею @Chris об использовании набора пар. Однако это не сработает, если значения не все различны. Использование функтора, который сравнивает только второй элемент пары, не помогает. Если вы сначала введете make_pair(1, 1) в набор, а затем попытаетесь вставить make_pair(2, 1), тогда вторая пара не будет вставлена, потому что обе пары считаются идентичными этому набору. Вы можете увидеть этот эффект здесь, на Ideone.

Ответ 7

Создайте еще одну карту, предоставите функцию less(), основанную на значении not key, и функция должна вернуть true, если значение1 < = значение2 (не строго <). В этом случае элементы с нечеткими значениями также могут быть отсортированы.

Ответ 8

Я нашел это в этом указателе. В примере сортируется std :: map <std :: string, int> по всем значениям int.

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