Menu

C++ set: eindeutige, sortierte Elemente mit std::set

Wie std::set in C++ eindeutige, automatisch sortierte Werte speichert: Einfügen, Mitgliedschaft mit count und find prüfen, in Reihenfolge iterieren und die Unterschiede zwischen set, multiset und unordered_set.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Eine Sammlung eindeutiger, sortierter Werte

Du hast gesehen, wie unordered_map Schlüssel-Wert-Paare mit schneller hashbasierter Suche speichert. Ein std::set ist der einfachere Cousin: Es speichert nur Werte – keine zugehörigen Daten – und erzwingt zwei Regeln automatisch. Jedes Element ist eindeutig (Duplikate werden stillschweigend verworfen) und die Elemente werden stets in sortierter Reihenfolge gehalten.

Damit ist set die natürliche Wahl für Fragen wie „Habe ich das schon einmal gesehen?" oder „Gib mir die verschiedenen Elemente in sortierter Reihenfolge." Du fügst ein, ohne dir Gedanken über Duplikate zu machen, und iterierst, ohne vorher zu sortieren.

Beachte, dass wir 10 zweimal und in falscher Reihenfolge eingefügt haben, und trotzdem ist die Ausgabe sortiert und 10 erscheint nur einmal. Das set hat die Buchführung für dich übernommen.

Einfügen und Entfernen

insert fügt einen Wert hinzu, wenn er noch nicht vorhanden ist. Es gibt ein pair zurück, dessen .second ein bool ist, der dir sagt, ob das Einfügen tatsächlich stattgefunden hat – praktisch, wenn du wissen willst, ob ein Wert neu war:

Um einen Wert zu entfernen, rufe erase mit dem Wert selbst auf – es gibt die Anzahl der entfernten Elemente zurück (0 oder 1 bei einem set). Etwas zu löschen, das nicht vorhanden ist, ist harmlos und kein Fehler:

Mitgliedschaft prüfen

Der ganze Sinn eines set sind schnelle „Ist das hier drin?"-Tests. Am klarsten geht das mit count, das 1 oder 0 zurückgibt:

Seit C++20 gibt es eine noch besser lesbare Option, contains, die direkt einen bool zurückgibt:

if (primes.contains(7)) { /* ... */ }   // C++20

Ein häufiger Fehler ist, zu operator[] zu greifen, wie man es bei einer map täte. Ein set hat kein operator[] – es gibt keinen Wert zum Abrufen, nur das Vorhandensein zum Prüfen. Verwende count oder contains, nicht s[7].

Wenn du die tatsächliche Position brauchst (um sie zu löschen oder die Nachbarn anzusehen), verwende find, das einen Iterator oder end() zurückgibt:

Sortierte Iteration und Bereichsabfragen

Da ein set sortiert ist, liefert die Iteration die Elemente immer vom kleinsten zum größten, und du bekommst die Tricks sortierter Container gratis dazu. lower_bound(x) liefert das erste Element, das nicht kleiner als x ist, und upper_bound(x) das erste Element, das echt größer als x ist – zusammen erlauben sie dir, einen Zahlenbereich zu durchlaufen, ohne jedes Element zu prüfen:

Eine subtile, aber wichtige Regel: set-Elemente sind unveränderlich. Der Iterator gibt dir eine const-Referenz, du kannst ein Element also nicht an Ort und Stelle ändern – das könnte die Sortierreihenfolge zerstören, auf die sich der Container verlässt. Um einen Wert zu „ändern", lösche den alten und füge den neuen ein.

Standardmäßig ist die Sortierung aufsteigend (std::less). Für eine absteigende Reihenfolge gib einen anderen Vergleichsoperator als zweites Template-Argument an:

set vs multiset vs unordered_set

std::set ist einer von drei engen Verwandten, und die richtige Wahl ist wichtig:

set<int>            // eindeutige Werte, sortiert, O(log n)
multiset<int>       // erlaubt Duplikate, sortiert, O(log n)
unordered_set<int>  // eindeutige Werte, KEINE Reihenfolge, durchschnittlich O(1)

Greife zu unordered_set, wenn du nur Mitgliedschaftstests brauchst und die Reihenfolge egal ist – seine hashbasierten Suchen sind im Durchschnitt schneller als das baumbasierte O(log n) des set. Wähle set, wenn du Elemente in sortierter Reihenfolge, Bereichsabfragen mit lower_bound/upper_bound oder stabiles Iteratorverhalten brauchst. Verwende multiset nur, wenn Duplikate von Bedeutung sind (zum Beispiel ein Histogramm wiederholter Werte): In einem multiset kann count(x) mehr als 1 zurückgeben, und erase(x) entfernt alle Kopien, sofern du nicht über einen einzelnen Iterator löschst.

Ein klassischer Einsatz von set: einen vector in einem Schritt entduplizieren und sortieren.

Das set aus den Iteratoren des vector zu konstruieren, verwirft jedes Duplikat und sortiert den Rest – keine manuelle Schleife, kein std::sort-plus-std::unique-Tanz.

Als Nächstes: pair und tuple

Du hast jetzt gesehen, wie .first und .second auf dem pair auftauchen, das insert zurückgibt, und wie strukturierte Bindungen (auto [it, inserted]) es sauber entpacken. Diese leichtgewichtigen Typen zum „Bündeln einiger Werte" sind überall in der STL. Als Nächstes betrachten wir pair und tuple direkt – wie man sie erstellt, entpackt und mehrere Werte aus einer Funktion zurückgibt, ohne eine ganze struct zu definieren.

Häufig gestellte Fragen

Was ist ein set in C++?

Ein std::set ist ein assoziativer Container, der eindeutige Werte in sortierter Reihenfolge speichert. Das Einfügen eines bereits vorhandenen Werts bewirkt nichts, und beim Iterieren werden die Elemente vom kleinsten zum größten durchlaufen. Suchen, Einfügen und Löschen sind alle O(log n), weil ein balancierter binärer Suchbaum dahintersteht.

Wie prüfe ich, ob ein Element in einem C++-set vorhanden ist?

Verwende s.count(x), das 1 zurückgibt, wenn x vorhanden ist, und sonst 0, oder s.contains(x) in C++20, das einen bool zurückgibt. Vermeide s.find(x) != s.end(), es sei denn, du brauchst den Iterator wirklich – es kostet gleich viel, ist aber umständlicher.

Was ist der Unterschied zwischen set und unordered_set in C++?

std::set hält die Elemente sortiert und bietet O(log n)-Operationen; std::unordered_set hält sie über eine Hash-Tabelle in keiner bestimmten Reihenfolge mit durchschnittlich O(1)-Operationen. Verwende set, wenn du eine sortierte Iteration oder Bereichsabfragen brauchst, und unordered_set, wenn du nur schnelle Mitgliedschaftstests brauchst und die Reihenfolge egal ist.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S