cpp wie man mit Karten vergleicht


Antwort 1:

Mathematisch gesehen ist eine Karte ein Modell, das einen Schlüssel nimmt und mit einem Wert antwortet. Ein Set ist ein Spezialfall einer Karte, bei dem der Schlüssel mit dem Wert identisch ist, dem er zugeordnet ist. Dieses Konzept erstreckt sich auf die C ++ - Implementierung dieser beiden Objekte.

Wann ist std :: map nützlich: Stellen Sie sich eine Datenbank vor, in der Benutzerkonten gespeichert sind. Nehmen wir an, es handelt sich um Netflix-Konten. Die Karte, die diese Tabelle von Netflix-Benutzern darstellt, erhält möglicherweise eine Benutzer-ID und gibt das für diese ID-Nummer relevante Benutzerobjekt zurück. Wir können dann die ID verwenden, um das Benutzerkonto in O (1) -Zeit abzurufen, indem wir die eindeutige Benutzer-ID mit dem Konto verknüpfen, und um nach einem Konto zu suchen, benötigen Sie lediglich die ID, um alle diese Benutzerdaten abzurufen.

Können wir das mit std :: set machen? Wie können wir dieses vorherige Beispiel mit einer Menge implementieren? Wir können Benutzerkonten in unser std :: set einfügen, aber wenn wir nach einem Benutzerkonto suchen möchten, benötigen wir das gesamte Benutzerkonto, um eine Suche nach unserem Benutzerdatensatz durchzuführen. Warten Sie eine Minute, wenn wir die Benutzerkontoinformationen bereits haben, warum sollten wir danach suchen müssen? Was ist auch, wenn wir nur die Benutzer-ID-Nummer haben, wir das Konto nicht mehr in O (1) Zeit abrufen können, müssen wir die gesamte Tabelle durchlaufen, um das passende Konto zu finden, und dies wird O (N) sein.

Wann möchten Sie möglicherweise std :: map verwenden und wann möchten Sie std :: map für Grundelemente verwenden? Stellen Sie sich eine Struktur vor, in der alle Primzahlen unter einem beliebigen Wert N gespeichert werden sollen. Wenn wir nur wissen möchten, ob eine Zahl ein Mitglied der Primzahl ist, ist std :: set sinnvoll, und wir möchten möglicherweise einfach überprüfen, ob eine beliebige Ganzzahl vorliegt Der Wert 'k' ist in unserer Menge vorhanden, und wir sind möglicherweise nicht an der Vorstellung eines Index oder eines Schlüssels interessiert. Wenn wir jedoch an der Folge von Primzahlen interessiert sind, möchten wir möglicherweise eine Schlüssel-Wert-Paarung. Vielleicht möchten wir die Folge p = {2, 3, 5, 7, 11,…} modellieren und möchten wissen, ob p [j] = k für einige beliebige Werte von j & k ist. In diesem letzteren Fall möchten wir vielleicht std :: map in Betracht ziehen (obwohl std :: vector auch eine gute Wahl sein könnte).

Also kurz gesagt:

  1. Eine festgelegte Datenstruktur ist ein spezialisierter Typ einer Kartendatenstruktur, bei der key == value und die C ++ STL-Definitionen dieses Konzept widerspiegeln
  2. std :: map als std :: set ist im Allgemeinen nützlicher, da std :: map eine schnelle Suche nach Schlüsselwerten nach Aggregatwerten ermöglicht (ganze Objekte anstelle von Grundelementen).
  3. std :: set ist für Grundelemente oft nützlicher, da die Abbildung eines Grundwerts k-> k eher willkürlich ist

Antwort 2:

Viele der Antworten beziehen sich nicht auf die Änderung der Implementierung von Map- und Set-Containern für C ++ 17, auch wenn dies relevant ist. Daher werde ich mich ausschließlich damit befassen.

Wie können Sie einen Knoten von einem Container in einen anderen verschieben?

Wie können Sie den Schlüssel eines Kartenknotens ändern?

Wenn Sie bewegliche, aber nicht kopierbare Daten in eine Karte oder einen Satz eingefügt haben, wie können Sie sie wieder herausholen?

Diese werden alle in C ++ 17 durch Hinzufügen einer neuen undurchsichtigen Datenstruktur… des Knotenhandles… und zweier neuer Elementfunktionen für jeden Satz oder Kartencontainer, die mit Knotenhandles arbeiten… beantwortet, beantwortet, wodurch ein Knoten aus einer Karte oder extrahiert wird in ein Knotenhandle gesetzt und eine neue Überladung von Einfügungen, die einen Knoten von einem Knotenhandle nimmt und ihn in eine Karte oder einen Satz einfügt. Es gibt auch eine neue Komfortmethode, das Zusammenführen, bei der alle nicht doppelten Knoten in einer Karte verschoben oder in eine andere gesetzt werden.

Knotenhandle (C ++ 17)

Ein Knotenhandle hat mehrere Eigenschaften. Es besitzt einen extrahierten Knoten auf genau die gleiche Weise wie std :: unique_ptr und ist daher auch beweglich, aber nicht kopierbar. Wenn ein Knotenhandle einen Knoten besitzt, wenn er zerstört wird, wird der Knoten zerstört und freigegeben. Dies bedeutet, dass er auch eine Kopie des Allokators in seinem Container haben muss, um die Freigabe durchzuführen. Dies bedeutet, dass die in zwei Containern verwendeten Allokatoren gleich vergleichen müssen (operator == gibt true zurück), damit ein Knoten von einem Container in den anderen verschoben werden kann. Alle Standardzuordnungen sind gleich, daher ist dies normalerweise kein Problem.

Während ein Knoten einem Knotenhandle nh gehört, kann er auf eine Weise bearbeitet werden, die nicht möglich ist, während er sich in einer Karte oder einem Satz befindet. Beispielsweise kann der Schlüssel eines extrahierten Kartenknotens durch Zuweisen zu nh.key () geändert und ein nicht kopierbarer zugeordneter Wert mit std :: move (nh.mapped ()) verschoben werden. Ein extrahierter Mengenknoten kann einen nicht kopierbaren Wert haben, der mit std :: move (nh.value ()) verschoben wurde.


Antwort 3:

Derzeit bietet C ++ 8 Standard

assoziative Container

in diesem Raum:

  • std :: set
  • std :: multiset
  • std :: unordered_set
  • std :: unordered_multiset
  • std :: map
  • std :: multimap
  • std :: unordered_map
  • std :: unordered_multimap

Sie werden vielleicht feststellen, dass es hier drei Variationsachsen gibt:

  • Set gegen Karte.
  • bestellt gegen ungeordnet.
  • eindeutiger Schlüssel gegen Mehrfachschlüssel.

Sie haben nach Set vs. Map gefragt. Es lohnt sich jedoch zu wissen, wie man aus allen 8 Kombinationen auswählt.

Set gegen Karte

Ein Satz enthält einen Satz Schlüssel. Sie können Schlüssel in den Satz einfügen und daraus entfernen, testen, ob der Schlüssel im Satz vorhanden ist, und den Satz aller Schlüssel durchlaufen. Sobald ein Schlüssel in das Set eingefügt wurde, ist der Schlüssel unveränderlich. Sie können einen Schlüssel nach dem Einfügen nicht mehr ändern. Sie müssen ihn vielmehr löschen und den neuen Schlüssel einfügen, wenn Sie einen vorhandenen Schlüssel ändern möchten.

Eine Karte ordnet jedem Schlüssel einen Wert zu. Werte können sich vom Schlüssel selbst unterscheiden. Normalerweise sind Werte auch veränderlich. Ich kann einen Wert anhand seines Schlüssels nachschlagen und den Wert ändern, sobald ich ihn gefunden habe.

Sie möchten set verwenden, wenn Sie sich nur um den Schlüsselsatz kümmern, den Sie haben. Sie möchten map verwenden, wenn Sie eine Reihe von Werten verfolgen möchten, denen Schlüssel zugeordnet sind.

Angenommen, ich wollte alle Personen im Auge behalten, die an einem Meeting teilnehmen. Ein Set könnte dafür angemessen sein. Jeder Teilnehmer ist Mitglied des Sets, und ich kann alle Mitglieder des Sets durchlaufen, um eine Liste der Teilnehmer zu erstellen.

Angenommen, mein Meeting ist verpflegt, und ich möchte die Essenspräferenzen aller an meinem Meeting teilnehmenden Personen verfolgen. Jetzt möchte ich eine Karte der Teilnehmer zu den Essenspräferenzen. Der Schlüssel in diesem Fall ist der Teilnehmer, und der Wert ist die Essenspräferenz. Sie können die Essenspräferenzen jedes Teilnehmers ändern, ohne den Teilnehmer selbst zu ändern. (Auf diese Weise ist es weniger umständlich…)

bestellt gegen ungeordnet

Die assoziativen Container ohne

ungeordnet

im Namensangebot

O (\ lg n)

Zugriffszeit. Sie benötigen Schlüssel, die sind

Vergleichbar

und

Streng schwach bestellt.

Sie bestehen normalerweise aus ausgeglichenen binären Suchbäumen. Wenn Sie alle Elemente durchlaufen, werden Sie die Schlüssel in nicht absteigender Reihenfolge aufrufen. (Oder nicht aufsteigende Reihenfolge, wenn Sie umgekehrte Iteratoren verwenden.)

Die assoziativen Container mit ungeordnetem Namen bieten eine amortisierte O (1) -Zugriffszeit, vorausgesetzt, Sie können eine O (1) -Hashing-Funktion für Ihren Schlüssel erstellen. Umgangssprachlich werden diese als Hash-Tabellen bezeichnet. Sie benötigen eine effiziente Hashing-Funktion, damit die ungeordneten Container effizient arbeiten können. Wenn Sie alle Elemente durchlaufen, besuchen Sie die Schlüssel in beliebiger Reihenfolge.

Wann sollten Sie bestellt oder ungeordnet verwenden? Es kommt auf ein paar Dinge an:

  • Müssen Sie häufig alle Schlüssel in einer deterministischen Reihenfolge besuchen? In diesem Fall ist ein bestellter Container möglicherweise eine vernünftige Wahl.
  • Ist der Vergleich schneller oder langsamer als das Hashing? Wenn es viel schneller ist, ist die Bestellung möglicherweise besser. Wenn es viel langsamer ist, kann ungeordnet schneller sein.
  • Kennen Sie die ungefähre Gesamtgröße des Containers im Voraus? Das Ändern der Größe eines ungeordneten Containers kann teuer sein, während das Einfügen in einen bestellten Container keine wilden Leistungsschwankungen aufweist.
  • Welchen Speicherbedarf können Sie tolerieren? Ungeordnete Container neigen dazu, Größe gegen Geschwindigkeit zu tauschen.

Wenn Sie Ihren Code sorgfältig schreiben, können Sie versuchen, zwischen bestellten und ungeordneten Containern zu einem Benchmark zu wechseln, der für Ihre spezielle Arbeitslast eine bessere Leistung erbringt.

Eine Anwendung, die ich geschrieben habe, ergab eine interessante Mischung aus bestellten und ungeordneten Containern, die auf einem solchen Benchmarking basiert. Ich war ein wenig überrascht, welche Container wo gewonnen haben und wie sich das änderte, als ich die Eigenschaften der Schlüssel änderte. Insbesondere der Wechsel von std :: string zu einer durch Ganzzahlen indizierten Zeichenfolgentabelle hat die Kosten für Hashing-Funktionen spürbar verändert.

eindeutiger Schlüssel gegen Mehrfachschlüssel

Die assoziativen Container ohne Multi im Namen erlauben nur eine einzige Instanz jedes Schlüssels im Container. Das heißt, jeder Schlüssel muss eindeutig sein. Dies bietet eine ähnliche Semantik wie ein 1-D-Array, bei dem jeder Index ein Element enthält. Das Einfügen eines Schlüssels, der bereits im Container vorhanden ist, ist ein logischer Fehler.

Die assoziativen Container mit dem Namen multi ermöglichen mehrere Instanzen jedes Schlüssels. Sie können jede Taste so oft einfügen, wie Sie möchten. Die wiederholten Schlüssel werden in der Einfügereihenfolge beibehalten.

Hinweis: Dies ist auch für Multisets von Bedeutung, da die Vergleichskriterien Äquivalent von Gleich unterscheiden. Zwei Schlüssel sind gleichwertig, wenn keiner weniger als der andere vergleicht. Die Vergleichsfunktion ist jedoch nicht erforderlich, um alle Felder des Schlüsselobjekts zu berücksichtigen.

Welches solltest du wählen? Es hängt wirklich von dem Problem ab, das Sie lösen möchten. Anekdotisch habe ich selten Multisets oder Multimaps benötigt.

Wenn Sie alle angezeigten Instanzen von 'key' im Auge behalten müssen, unabhängig davon, ob einige von ihnen gleich sind oder nicht, ist ein Multiset oder eine Multimap die richtige Wahl. Andernfalls möchten Sie wahrscheinlich die Nicht-Multi-Sets oder Karten.

Eine interessante Verwendung für std :: multiset oder std :: multimap ist die Prioritätswarteschlange. Verwenden Sie die Priorität als Schlüssel. Das von begin () zurückgegebene Element ist Ihr Element mit der höchsten Priorität. Die Liste der Elemente wird in sortierter Reihenfolge verwaltet. Wenn Sie also einen Iterator haben, der auf ein Element zeigt, können Sie schnell feststellen, was unmittelbar davor und unmittelbar danach liegt. Insbesondere wenn die Priorität tatsächlich durch die Zeit dargestellt wird, dh diese Prioritätswarteschlange ist tatsächlich eine zeitlich geordnete Ereigniswarteschlange, können Sie kostengünstig bestimmen, welche Ereignisse in der Nähe einer bestimmten Zeit geplant sind.

Dies funktioniert jedoch nur mit dem bestellten Multiset und der bestellten Multimap.

std :: priority_queue

Dies ist möglicherweise die bessere Wahl, wenn Sie nur einen schnellen Zugriff auf das Element mit der höchsten Priorität benötigen und nicht von der vollständigen Sortierung eines Multisets oder einer Multimap profitieren. (Sehen

std :: priority_queue

für mehr Details.)


Antwort 4:

Nun, das kann ich beantworten.

Karten

Karten erfordern Schlüssel. Für jeden Schlüssel haben Sie bestimmte Werte.

Ein Schlüssel kann nun alles sein, eine Ganzzahl, eine Zeichenfolge oder sogar ein Objekt (mit zusätzlichen Vergleichsfunktionen ausgestattet). So können die Werte sein.

Schau dir das an:

Karte M; // Ganzzahlschlüssel - GanzzahlwertM [3] = 2;Karte S; // string key - ganzzahliger WertS ["ahar"] = 26;

Das ist faszinierend.

Angenommen, Sie möchten die Geburtstage Ihrer Freunde speichern. Sie deklarieren einfach eine Karte und speichern Sie ihre Namen und Geburtsdaten nur durch eine einfache Zuordnung. Es ist wie ein Wörterbuch in Python.

Sets

Dies ist bei Sets nicht der Fall. Sets benötigen keine Paarung (Schlüssel, Wert). Sie enthalten nur die Werte, die sie enthalten sollen (natürlich mit Vergleichsfunktionen oder Operatorüberladung bei Bedarf). Zum Beispiel:

einstellen S;S. Einfügung (13);einstellen T;S.insert ("ahar");einstellen X;S.insert (yourObject);

Aus Sicht der Leistung haben sie Ähnlichkeiten. Suchen, Einfügen, Löschen usw. sind in der Reihenfolge von

O (logn)

in beiden (danke geht an

Roter schwarzer Baum

, das, wovor Sie in Ihrem Datenstrukturkurs Angst hatten: P). Aufgrund der Implementierungs- und Nutzungsunterschiede kann es jedoch zu bestimmten Gemeinkosten kommen.

Weiterer Hinweis:

Wenn Sie eine Beobachtung machen, werden Sie feststellen, dass sich Mengen und Karten aus der grundlegenden strukturellen Perspektive unterscheiden. Die Frage, die Sie gestellt haben, kann also etwas interessanter sein, wenn Sie sie so betrachten, dass Sie Sets als Alternative zu Karten verwenden können und umgekehrt.

Dies kann uns zu einer interessanten Frage führen: Was ist der Unterschied zwischen der Menge? > und Karte ? Dies ist eine ziemlich berechtigte Frage, da Sie in diesem Szenario an das erste Element des Paares in der Menge denken können, das dem Schlüssel in der Karte entspricht!

Beispielsweise:

Karte M;M.insert ({"motta", 13});einstellen > S;S.insert ({"motta", 13});

Sie können sehen, dass der oben beschriebene Satz eine mögliche Alternative zur Karte sein kann.

Sind sie also gleichwertig? Nun, nein.

Erstens kann die Karte keine unterschiedliche Ganzzahl für denselben Schlüssel enthalten, wie wir oben deklariert haben. Aber setzen kann.

So,

M.insert ({"ahar", 13)};S.insert ({"ahar", 13});M.insert ({"ahar", 26});S.insert ({"ahar", 26});

macht die Größe des Sets gleich 2, aber für die Karte ist es 1.

Zweitens sollten Sie bereits wissen, dass diese C ++ - Container Iteratoren verwenden, mit denen Elemente in diesen Containern angezeigt werden. Um es einfach zu sagen, Iteratoren sind das, was Sie benötigen, um auf die Daten in diesen Containern zuzugreifen.

Nun sieh dir das an:

einstellen > S;S.insert ({"ahar", 26});auto it = S.begin (); // es ist jetzt ein Iterator für ("ahar", 26)

Aus irgendeinem Grund beabsichtigen Sie, den Wert des entsprechenden von ihm iterierten Paares von 26 auf 13 zu ändern. Versuchen Sie Folgendes:

it-> second = 13;

Ähm ... nein. Das kannst du nicht tun.

Der Grund ist etwas kompliziert. Einfach ausgedrückt sind die Iteratoren für C ++ - Sätze wie konstante Iteratoren. Sie können also nicht einfach den entsprechenden Datenwert direkt ändern. Sie müssen es aus dem Set löschen und dann Ihren neuen Wert wie folgt hinzufügen:

S.erase (es);Paar p = {"ahar", 13};S. Einfügung (p);

: |

Bei Karten gilt dies vollständig:

Karte M;M.insert ({"motta", 13});auto it = M.begin ();it-> second = 26;

Ich hoffe ich habe alles richtig gemacht. : P.

Danke fürs Lesen.


Antwort 5:

Eine Karte ist eine Datenstruktur, mit der Werte nach Schlüsseln gesucht werden, während eine Menge nur eine Sammlung von Werten ist.

In Bezug auf die Implementierung werden sie normalerweise nicht so unterschiedlich implementiert, und beide verwenden normalerweise Rot-Schwarz-Bäume unter der Haube, um für die meisten Operationen eine logarithmische Zeitkomplexität zu erhalten. Ein Unterschied, abhängig von den Implementierungen, besteht darin, dass eine Menge ein rot-schwarzer Baum von Elementen ist, während eine Karte ein rot-schwarzer Baum von (Schlüssel-, Wert-) Tupelelementen ist, sortiert nach dem ersten Element (dem Schlüssel) in der Tupel.


Antwort 6:

Eine Karte ordnet ein Objekt einem anderen zu. Eine Menge ist eine geordnete Menge von Objekten. Eine Map wird häufig verwendet, um über einen Index auf Objekte zuzugreifen, sodass objectMap [i] das Objekt mit dem Index i enthält. Ein Satz kann zum Speichern von Objekten verwendet werden, mit dem zusätzlichen Vorteil, dass ein Satz identifiziert, ob ein Objekt bereits darin enthalten ist, und nur eine einzelne Entität der Objekte speichert. Sie können jedoch nur auf Objekte in einem Satz zugreifen, indem Sie darüber iterieren oder das erste oder letzte Element des Satzes abrufen.


Antwort 7:

std :: map ist ein assoziatives, geordnetes Array. Das bedeutet, dass es speichert Paare und durch den Schlüssel können Sie zum Wert gelangen. Sie können auch über die iterieren Paare und die Iteration folgen einer guten Reihenfolge der Schlüssel.

std :: set ist nur eine Sammlung von Werten. Wieder können Sie darüber iterieren und wieder folgt die Iteration einer guten Reihenfolge der Werte. Es gibt jedoch keine Assoziation wie oben. Sie können nur Fragen stellen wie "Ist dieser Wert im Set?" Und dafür müssen Sie bereits den fraglichen Wert haben.