it-swarm.com.de

std :: map, wie nach Wert und dann nach Schlüssel sortiert wird

Ich muss eine Karte nach Wert und dann nach Schlüssel sortieren. Ich habe eine Karte mit solchen Inhalten ...

  1  realistically
  8         really
  4         reason
  3     reasonable
  1     reasonably
  1     reassemble
  1    reassembled
  2      recognize
 92         record
 48        records
  7           recs

Ich brauche die Werte in der richtigen Reihenfolge, aber der Kicker ist, dass die Schlüssel in alphabetischer Reihenfolge sein müssen, nachdem die Werte in Ordnung sind. Wie gehen Sie am besten vor?

35
Trevor Hutto

std::map sortiert seine Elemente nach keys. Es kümmert sich nicht um die values beim Sortieren.

Sie können std::vector<std::pair<K,V>> verwenden und dann mit std::sort sortieren, gefolgt von std::stable_sort:

std::vector<std::pair<K,V>> items;

//fill items

//sort by value using std::sort
std::sort(items.begin(), items.end(), value_comparer);

//sort by key using std::stable_sort
std::stable_sort(items.begin(), items.end(), key_comparer);

Die erste Sortierung sollte std::sort verwenden, da es nlog(n) ist, und dann std::stable_sort verwenden, was im schlimmsten Fall n(log(n))^2 ist.

Beachten Sie, dass std::sort aus Gründen der Leistung ausgewählt ist, std::stable_sort jedoch für die richtige Reihenfolge erforderlich ist, da die Reihenfolge nach Wert beibehalten werden soll.


@gsf im Kommentar vermerkt, können Sie nurstd::sort verwenden, wenn Sie einen Vergleicher wählen, der values zuerst vergleicht, und wenn sie gleich sind, sortieren Sie die keys.

auto cmp = [](std::pair<K,V> const & a, std::pair<K,V> const & b) 
{ 
     return a.second != b.second?  a.second < b.second : a.first < b.first;
};
std::sort(items.begin(), items.end(), cmp);

Das sollte effizient sein.

Aber warten Sie, es gibt einen besseren Ansatz: Speichern Sie std::pair<V,K> anstelle von std::pair<K,V>, und dann brauchen Sie überhaupt keinen Vergleich - der Standardvergleich für std::pair würde ausreichen, da er vergleicht first (das ist V) zuerst dann second das ist K:

std::vector<std::pair<V,K>> items;
//...
std::sort(items.begin(), items.end());

Das sollte klappen.

60
Nawaz

Sie können std::set anstelle von std::map verwenden.

Sie können sowohl Schlüssel als auch Wert in std::pair speichern, und der Typ des Containers sieht folgendermaßen aus:

std::set< std::pair<int, std::string> > items;

std::set sortiert seine Werte sowohl nach ursprünglichen Schlüsseln als auch nach Werten, die in std::map gespeichert wurden.

14
ks1322

Wie in Nawaz answer erläutert, können Sie Ihre Karte nicht nach Bedarf selbst sortieren, da std::map sortiert seine Elemente nur anhand der Schlüssel. Sie benötigen also einen anderen Container. Wenn Sie sich jedoch an Ihre Karte halten müssen, können Sie deren Inhalt (vorübergehend) in eine andere Datenstruktur kopieren.

Ich denke, die beste Lösung ist die Verwendung eines std::set Speichern von gespiegelten Schlüssel-Wert-Paaren gemäß ks1322's answer . Das std::set ist standardmäßig sortiert und die Reihenfolge der Paare ist genau so, wie Sie es brauchen:

3) Wenn lhs.first<rhs.first, gibt true zurück. Andernfalls, wenn rhs.first<lhs.first, gibt false zurück. Andernfalls, wenn lhs.second<rhs.second, gibt true zurück. Andernfalls wird false zurückgegeben.

Auf diese Weise brauchen Sie keinen zusätzlichen Sortierschritt und der resultierende Code ist ziemlich kurz:

std::map<std::string, int> m;  // Your original map.
m["realistically"] = 1;
m["really"]        = 8;
m["reason"]        = 4;
m["reasonable"]    = 3;
m["reasonably"]    = 1;
m["reassemble"]    = 1;
m["reassembled"]   = 1;
m["recognize"]     = 2;
m["record"]        = 92;
m["records"]       = 48;
m["recs"]          = 7;

std::set<std::pair<int, std::string>> s;  // The new (temporary) container.

for (auto const &kv : m)
    s.emplace(kv.second, kv.first);  // Flip the pairs.

for (auto const &vk : s)
    std::cout << std::setw(3) << vk.first << std::setw(15) << vk.second << std::endl;

Ausgabe:

  1  realistically
  1     reasonably
  1     reassemble
  1    reassembled
  2      recognize
  3     reasonable
  4         reason
  7           recs
  8         really
 48        records
 92         record

Code auf Ideone

Hinweis: Seit C++ 17 können Sie bereichsbezogen für Schleifen zusammen mit strukturierte Bindungen zum Durchlaufen einer Karte verwenden. Dadurch wird der Code zum Kopieren Ihrer Karte noch kürzer und lesbarer:

for (auto const &[k, v] : m)
    s.emplace(v, k);  // Flip the pairs.
2
honk

std::map sortiert die Werte bereits anhand eines von Ihnen definierten Prädikats oder std::less, falls Sie keine angeben. std::set speichert auch Elemente in der Reihenfolge eines Definitionsvergleichers. Weder Set noch Map ermöglichen jedoch die Verwendung mehrerer Schlüssel. Ich würde vorschlagen, einen std::map<int,std::set<string> zu definieren, wenn Sie dies allein mit Ihrer Datenstruktur erreichen möchten. Sie sollten auch wissen, dass std::less für Zeichenfolge lexikographisch nicht alphabetisch sortiert wird.

1
rerun

EDIT: Die anderen beiden Antworten machen einen guten Punkt. Ich gehe davon aus, dass Sie sie in eine andere Struktur ordnen oder drucken möchten.

"Best" kann verschiedene Dinge bedeuten. Meinen Sie "am einfachsten", "am schnellsten", "am effizientesten", "am wenigsten Code", "am besten lesbar?"

Der naheliegendste Ansatz ist zweimal durchlaufen. Im ersten Durchlauf ordnen Sie die Werte an:

if(current_value > examined_value)
{
    current_value = examined_value
    (and then swap them, however you like)
}

Dann beim zweiten Durchlauf die Wörter alphabetisch schreiben, jedoch nur, wenn ihre Werte übereinstimmen.

if(current_value == examined_value)
{
    (alphabetize the two)
}

Streng genommen handelt es sich hierbei um eine "Blasensorte", die langsam ist, weil Sie jedes Mal, wenn Sie einen Swap durchführen, von vorne beginnen müssen. Ein "Pass" ist beendet, wenn Sie die gesamte Liste durchlaufen haben, ohne einen Swap durchzuführen.

Es gibt andere Sortieralgorithmen, aber das Prinzip wäre dasselbe: Nach Wert sortieren, dann alphabetisieren.

0
Matt