Ich habe derzeit eine std::map<std::string,int>
, die einen ganzzahligen Wert in einer eindeutigen Zeichenfolgenkennung speichert, und ich schaue mit der Zeichenfolge nach. Es macht meistens das, was ich will, außer dass es die Einfügereihenfolge nicht verfolgt. Wenn ich also die Karte iteriere, um die Werte auszudrucken, werden sie nach der Zeichenfolge sortiert. aber ich möchte, dass sie nach der Reihenfolge der (ersten) Einfügung sortiert werden.
Ich habe darüber nachgedacht, vector<pair<string,int>>
stattdessen a zu verwenden, aber ich muss die Zeichenfolge nachschlagen und die Ganzzahlwerte etwa 10.000.000 Mal erhöhen, damit ich nicht weiß, ob a std::vector
signifikant langsamer ist.
Gibt es eine Möglichkeit zur Verwendung std::map
oder gibt es einen anderen std
Behälter, der meinen Anforderungen besser entspricht?
[Ich bin auf GCC 3.4 und habe wahrscheinlich nicht mehr als 50 Wertepaare in meinem std::map
].
Vielen Dank.
quelle
Antworten:
Wenn Sie nur 50 Werte in std :: map haben, können Sie diese vor dem Ausdrucken in std :: vector kopieren und mit dem entsprechenden Funktor über std :: sort sortieren.
Oder Sie könnten boost :: multi_index verwenden . Es können mehrere Indizes verwendet werden. In Ihrem Fall könnte es folgendermaßen aussehen:
quelle
Sie können a
std::vector
mit astd::tr1::unordered_map
(einer Hash-Tabelle) kombinieren . Hier ist ein Link zur Dokumentation von Boost fürunordered_map
. Sie können den Vektor verwenden, um die Einfügereihenfolge zu verfolgen, und die Hash-Tabelle, um die häufigen Suchvorgänge durchzuführen. Wenn Sie Hunderttausende von Suchvorgängen durchführen, kann der Unterschied zwischen der Suche nach O (log n)std::map
und O (1) für eine Hash-Tabelle erheblich sein.quelle
std::map
das Arbeiten wie vorgesehen (dh durch Sortieren beim Einfügen selbst) und schnelle Laufzeit. (Ich habe dies gelesen, nachdem ich meine Version geschrieben habe, in der ich std :: list verwendet habe!)Halten Sie eine Parallele
list<string> insertionOrder
.Wenn es Zeit zum Drucken ist, iterieren Sie in der Liste und suchen Sie in der Karte .
quelle
std::string_view
für die Kartenschlüssel diestd::string
in derinsertionOrder
Liste angegebenen Schlüssel verwenden . Dies vermeidet das Kopieren, aber Sie müssen darauf achten, dass dieinsertionOrder
Elemente die Schlüssel in der Karte, die auf sie verweisen, überleben.Tessil hat eine sehr schöne Implementierung der bestellten Karte (und des bestellten Sets), bei der es sich um eine MIT-Lizenz handelt. Sie finden es hier: bestellte-Karte
Kartenbeispiel
quelle
Wenn Sie beide Suchstrategien benötigen, erhalten Sie zwei Container. Sie können a
vector
mit Ihren tatsächlichen Werten verwendenint
und einmap< string, vector< T >::difference_type>
daneben setzen , um den Index in den Vektor zurückzugeben.Um dies alles zu vervollständigen, können Sie beide in einer Klasse zusammenfassen.
Aber ich glaube, Boost hat einen Container mit mehreren Indizes.
quelle
Was Sie wollen (ohne auf Boost zurückzugreifen), ist das, was ich als "geordneten Hash" bezeichne. Dies ist im Wesentlichen ein Mashup aus einem Hash und einer verknüpften Liste mit Zeichenfolgen- oder Ganzzahlschlüsseln (oder beiden gleichzeitig). Ein geordneter Hash behält die Reihenfolge der Elemente während der Iteration mit der absoluten Leistung eines Hash bei.
Ich habe eine relativ neue C ++ - Snippet-Bibliothek zusammengestellt, die die Lücken in der C ++ - Sprache für C ++ - Bibliotheksentwickler ausfüllt. Gehe hier hin:
https://github.com/cubiclesoft/cross-platform-cpp
Greifen:
Wenn benutzergesteuerte Daten in den Hash eingefügt werden, möchten Sie möglicherweise auch:
Rufen Sie es auf:
Ich bin während meiner Recherchephase auf diesen SO-Thread gestoßen, um zu sehen, ob es so etwas wie OrderedHash bereits gibt, ohne dass ich in eine riesige Bibliothek gehen muss. Ich war enttäuscht. Also habe ich meine eigenen geschrieben. Und jetzt habe ich es geteilt.
quelle
Sie können dies nicht mit einer Karte tun, aber Sie können zwei separate Strukturen verwenden - die Karte und den Vektor, und sie synchronisieren - das heißt, wenn Sie aus der Karte löschen, das Element suchen und aus dem Vektor löschen. Oder Sie können ein
map<string, pair<int,int>>
- erstellen und in Ihrem Paar die Größe () der Karte beim Einfügen speichern, um die Position zusammen mit dem Wert des int aufzuzeichnen, und dann beim Drucken das Positionselement zum Sortieren verwenden.quelle
Eine andere Möglichkeit, dies zu implementieren, ist mit a
map
anstelle von avector
. Ich werde Ihnen diesen Ansatz zeigen und die Unterschiede diskutieren:Erstellen Sie einfach eine Klasse mit zwei Karten hinter den Kulissen.
Sie können dann einen Iterator
data_
in der richtigen Reihenfolge dem Iterator aussetzen . Die Artinsertion_order_
und Weise, wie Sie dies tun, wird durchlaufen , und für jedes Element, das Sie aus dieser Iteration erhalten, führen Sie eine Suche in derdata_
mit dem Wert von durchinsertion_order_
Sie können das effizientere
hash_map
für insertion_order verwenden, da es Ihnen nicht wichtig ist, direkt durchzugeheninsertion_order_
.Zum Einfügen können Sie eine Methode wie die folgende verwenden:
Es gibt viele Möglichkeiten, wie Sie das Design verbessern und sich um die Leistung sorgen können. Dies ist jedoch ein gutes Grundgerüst, um diese Funktionalität selbst zu implementieren. Sie können es als Vorlage erstellen und Paare tatsächlich als Werte in data_ speichern, damit Sie leicht auf den Eintrag in insertion_order_ verweisen können. Aber ich lasse diese Designprobleme als Übung :-).
Update : Ich denke, ich sollte etwas über die Effizienz der Verwendung von map vs. vector für insertion_order_ sagen
Wenn Sie weniger Löschvorgänge verwenden möchten, sollten Sie möglicherweise den Vektoransatz verwenden. Der Kartenansatz wäre besser, wenn Sie eine andere Reihenfolge (wie Priorität) anstelle der Einfügereihenfolge unterstützen würden.
quelle
Hier ist eine Lösung, die nur eine Standardvorlagenbibliothek erfordert, ohne den Multiindex von boost zu verwenden:
Sie können verwenden
std::map<std::string,int>;
undvector <data>;
wo in der Karte Sie den Index der Position von Daten in Vektoren speichern und Vektor speichert Daten in Einfügereihenfolge. Hier hat der Zugriff auf Daten eine O (log n) -Komplexität. Das Anzeigen von Daten in Einfügereihenfolge hat eine O (n) -Komplexität. Das Einfügen von Daten hat eine O (log n) -Komplexität.Beispielsweise:
quelle
Dies hängt etwas mit der Antwort von Faisals zusammen. Sie können einfach eine Wrapper-Klasse um eine Karte und einen Vektor erstellen und diese einfach synchronisieren. Durch die richtige Kapselung können Sie die Zugriffsmethode und damit den zu verwendenden Container steuern ... den Vektor oder die Karte. Dadurch wird die Verwendung von Boost oder Ähnlichem vermieden.
quelle
Eine Sache, die Sie berücksichtigen müssen, ist die geringe Anzahl von Datenelementen, die Sie verwenden. Es ist möglich, dass es schneller ist, nur den Vektor zu verwenden. Die Karte enthält einen gewissen Overhead, der dazu führen kann, dass die Suche in kleinen Datenmengen teurer ist als der einfachere Vektor. Wenn Sie also wissen, dass Sie immer ungefähr die gleiche Anzahl von Elementen verwenden, führen Sie ein Benchmarking durch und prüfen Sie, ob die Leistung der Karte und des Vektors so ist, wie Sie es wirklich glauben. Möglicherweise befindet sich die Suche in einem Vektor mit nur 50 Elementen in der Nähe der Karte.
quelle
// Sollte wie dieser Mann sein!
// Dadurch bleibt die Komplexität des Einfügens O (logN) und das Löschen ist auch O (logN).
quelle
Verwendung
boost::multi_index
mit Karten- und Listenindizes.quelle
Eine Zuordnung von Paaren (str, int) und statischen int, die beim Einfügen inkrementiert wird, indiziert Datenpaare. Fügen Sie eine Struktur ein, die den statischen Wert mit einem index () -Mitglied zurückgeben kann.
quelle