Frage C ++ std :: unordered_map Komplexität


Ich habe viel darüber gelesen ungeordnete_map  (c ++ 11)  Zeit-Komplexität hier bei stackoverflow, aber ich habe keine Antwort auf meine Frage gefunden.

Nehmen wir an, dass die Indexierung nach Integer erfolgt (nur zum Beispiel):

Einfügen / at-Funktionen arbeiten konstant (in der durchschnittlichen Zeit), so würde dieses Beispiel O (1) nehmen

std::unordered_map<int, int> mymap = {
            { 1, 1},
            { 100, 2},
            { 100000, 3 }
};

Was mich interessiert, ist, wie lange es dauert, alle (unsortierten) Werte zu durchlaufen, die in der Karte gespeichert sind - z.

for ( auto it = mymap.begin(); it != mymap.end(); ++it ) { ... }

Kann ich davon ausgehen, dass auf jeden gespeicherten Wert nur einmal (oder zweimal oder konstant) zugegriffen wird? Dies würde bedeuten, dass die Iteration durch alle Werte in der N-wertigen Karte O (N) erfolgt. Die andere Möglichkeit ist, dass mein Beispiel mit den Schlüsseln {1,10,100000} bis zu 1000000 Iteration dauern könnte (wenn es durch ein Array repräsentiert wird).

Gibt es einen anderen Container, der linear durchlaufen werden kann und auf den der Wert immer mit dem angegebenen Schlüssel zugreifen kann?

Was ich wirklich brauchen würde ist (Pseudocode)

myStructure.add(key, value) // O(1)
value = myStructure.at(key) // O(1)
for (auto key : mySructure) {...} // O(1) for each key/value pair = O(N) for N values

Ist std :: unordered_map die Struktur, die ich brauche?

Integer-Indizierung ist ausreichend, durchschnittliche Komplexität ebenfalls.


9
2017-10-26 18:40


Ursprung


Antworten:


Unabhängig davon, wie sie implementiert werden, bieten Standardcontainer Iteratoren, die die Iteratoranforderungen erfüllen. Das Inkrementieren eines Iterators muss eine konstante Zeit sein, also durch alle Elemente von Iterieren irgendein Standardbehälter ist O (N).


13
2017-10-26 18:56



Die Komplexitätsgarantien aller Standardcontainer sind in der C ++ Standard.

std::unordered_map Elementzugriff und Elementeinfügung müssen komplex sein O(1) im Durchschnitt und O(N) Worst Case (vgl. Abschnitte 23.5.4.3 und 23.5.4.4; Seiten 797-798).

Eine bestimmte Implementierung (dh die Implementierung der Standardbibliothek eines bestimmten Anbieters) kann die gewünschte Datenstruktur auswählen. Um jedoch dem Standard zu entsprechen, muss ihre Komplexität sein mindestens wie angegeben.


3
2017-10-26 18:51



Es gibt ein paar verschiedene Möglichkeiten, wie eine Hash-Tabelle implementiert werden kann, und ich schlage vor, Sie lesen mehr über diese, wenn Sie interessiert sind, aber die beiden wichtigsten sind durch Verkettung und offene Adressierung.

Im ersten Fall haben Sie ein Array von verknüpften Listen. Jeder Eintrag im Array kann leer sein, und jedes Element in der Hashtabelle befindet sich in einem Bucket. Also läuft die Iteration das Array hinunter und geht jede nicht leere Liste darin hinunter. Offensichtlich O (N), könnte aber je nach Zuordnung der verknüpften Listen möglicherweise sehr ineffizient sein.

Im zweiten Fall haben Sie nur ein sehr großes Array mit vielen leeren Slots. Hier ist die Iteration wiederum klar linear, könnte aber ineffizient sein, wenn die Tabelle größtenteils leer ist (was für Nachschlagezwecke sein sollte), weil die Elemente, die tatsächlich vorhanden sind, sich in verschiedenen Cache-Zeilen befinden.

So oder so, Sie werden lineare Iteration haben und Sie werden jedes Element genau einmal berühren. Beachten Sie, dass dies gilt für std::map Auch hier wird die Iteration linear sein. Aber im Fall der Karten ist die Iteration definitiv viel weniger effizient als die Iteration eines Vektors, also behalte das im Hinterkopf. Wenn Ihr Anwendungsfall BEIDE schnelle Suche und schnelle Iteration erfordert, wenn Sie alle Ihre Elemente im Voraus einfügen und nie löschen, könnte es viel besser sein, sowohl die Karte als auch den Vektor zu haben. Nimm den zusätzlichen Platz für die zusätzliche Leistung.


2
2017-10-26 18:50