Menu

C++ Sortieren: std::sort, eigene Vergleichsfunktionen & stabiles Sortieren

Sortiere Vektoren und Arrays in C++ mit std::sort: Standardreihenfolge, eigene Vergleichsfunktionen, Structs nach einem Feld sortieren und die Strict-Weak-Ordering-Falle, die Abstürze verursacht.

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

Sortieren ist nur ein weiterer Algorithmus

Auf der vorherigen Seite hast du gesehen, dass die Standardbibliothek fertige Algorithmen mitbringt, die über Iteratoren auf jedem Bereich funktionieren. Sortieren ist der Algorithmus, zu dem du am häufigsten greifst, und er bekommt seine eigene Seite, weil er ein paar scharfe Kanten hat: eigene Ordnungen, Stabilität und eine Regel, die dir, wenn du sie brichst, undefiniertes Verhalten statt einer falschen Antwort beschert.

Das Arbeitstier ist std::sort aus <algorithm>. Du gibst ihm den Anfang und das Ende eines Bereichs, und es ordnet die Elemente an Ort und Stelle in aufsteigender Reihenfolge um:

Es wird keine Kopie erstellt - der Vektor selbst wird umgeordnet. Unter der Haube ist std::sort typischerweise ein Introsort (Quicksort, der auf Heapsort zurückfällt) und liefert dir im Durchschnitt O(n log n). Das ist fast immer schneller und weit weniger fehleranfällig, als deine eigene Sortierung zu schreiben.

Es funktioniert auch mit einfachen C-Arrays - du beschreibst den Bereich einfach mit Zeigern:

Eigene Reihenfolge mit einer Vergleichsfunktion

Standardmäßig ordnet std::sort die Elemente mit operator<. Um anders zu sortieren, übergib ein drittes Argument: eine Vergleichsfunktion, die zwei Elemente nimmt und true zurückgibt, wenn das erste vor dem zweiten kommen soll.

Ein Lambda passt hier natürlich. Absteigende Reihenfolge ist einfach a > b:

Für den häufigen Fall einer einfachen absteigenden Reihenfolge bei eingebauten Typen liefert die Bibliothek sogar eine fertige Vergleichsfunktion mit, greater<T>() aus <functional>:

#include <functional>
sort(nums.begin(), nums.end(), greater<int>());  // dasselbe wie a > b

Die Vergleichsfunktion ist auch der Weg, nach etwas anderem als dem Wert selbst zu sortieren - zum Beispiel Strings nach Länge statt alphabetisch zu sortieren:

Nimm die Parameter der Vergleichsfunktion per const& für alles, was größer als ein paar Bytes ist (wie string) - jedes Element bei jedem Vergleich zu kopieren ist reine Verschwendung.

Structs nach einem Feld sortieren

In echten Programmen sortierst du normalerweise Sammlungen von Structs nach einem ihrer Felder. Die Vergleichsfunktion greift einfach auf das Feld zu, das dich interessiert. Hier sortieren wir Personen nach Alter, die jüngsten zuerst:

Beachte, dass Linus und Dennis beide 25 Jahre alt sind. Sie kamen hier in ihrer ursprünglichen relativen Reihenfolge heraus, aber std::sort garantiert das nicht. Wenn die relative Reihenfolge gleicher Elemente wichtig ist, verwende std::stable_sort, das sie erhält (zu geringen Performance-Kosten):

Um Gleichstände gezielt aufzulösen - etwa nach Alter und dann alphabetisch nach Name sortieren - vergleiche den sekundären Schlüssel nur dann, wenn die primären Schlüssel gleich sind. std::tie macht das übersichtlich:

Die Strict-Weak-Ordering-Falle

Das ist der mit Abstand gefährlichste Fehler beim Sortieren in C++, denn er gibt dir keine falsche Antwort - er gibt dir undefiniertes Verhalten, was oft einen Absturz oder einen Lesezugriff außerhalb der Grenzen bedeutet.

std::sort verlangt, dass deine Vergleichsfunktion eine strikte schwache Ordnung definiert. Die praktische Regel: comp(x, x) muss false sein für jedes Element x. Mit anderen Worten: Ein Element ist nie „vor" sich selbst. Genau das geben dir < und >, und genau das brechen <= und >=:

// BUG: gibt true zurück, wenn a == b, und verletzt damit die strikte schwache Ordnung.
sort(v.begin(), v.end(), [](int a, int b) {
    return a <= b;   // undefiniertes Verhalten - kann bei manchen Eingaben abstürzen
});

Mit <= behauptet die Vergleichsfunktion, dass 5 vor einer anderen 5 kommt, was widersprüchlich ist. std::sort kann dann einen Zeiger über das Ende des Bereichs hinaus laufen lassen. Kleine Eingaben scheinen manchmal zu funktionieren, was diesen Bug furchteinflößend macht - er kann deine Tests bestehen und in der Produktion abstürzen. Die Lösung ist schlicht <:

Ein zweiter Klassiker: Sortieren macht alles ungültig, das in den Bereich hineinzeigt. Iteratoren, Zeiger und Indizes, die du vor dem Sortieren gespeichert hast, verweisen danach nicht mehr auf dasselbe logische Element, weil sich die Elemente bewegt haben. Leite jede Position, die du brauchst, nach dem Sortieren neu ab, niemals vorher.

Einen Teil eines Bereichs sortieren

Manchmal musst du nicht alles sortieren - du willst nur die obersten paar. Den ganzen Vektor zu sortieren, um die ersten drei zu lesen, ist Verschwendung. std::partial_sort ordnet nur die Elemente, die du verlangst, und lässt den Rest in unspezifizierter Reihenfolge, was günstiger ist:

Und wenn du nur das eine Element brauchst, das an einer bestimmten Position stehen würde - wie der Median - macht std::nth_element noch weniger Arbeit: Es platziert das richtige Element an diesem Index, mit allem Kleineren davor und allem Größeren dahinter, alles im Durchschnitt in O(n).

Greife zu diesen, wenn „vollständig sortiert" mehr ist, als das Problem tatsächlich braucht - sie sparen bei großen Datenmengen echte Zeit.

Als Nächstes: Templates

Ist dir aufgefallen, dass dasselbe std::sort mit int, string und deinem eigenen Person-Struct umgehen konnte, und dass greater<int>() genauso gut greater<string>() sein könnte? Diese Allgemeingültigkeit ist keine Magie - es sind Templates, der Mechanismus, der ein einziges Stück Code für jeden Typ funktionieren lässt, den der Aufrufer einsteckt. Auf der nächsten Seite sehen wir, wie du deine eigenen templatisierten Funktionen und Klassen schreibst, damit dein Code so typunabhängig sein kann wie die Algorithmen, die du verwendet hast.

Häufig gestellte Fragen

Wie sortiert man einen Vektor in C++?

Binde <algorithm> ein und rufe sort(v.begin(), v.end()) auf. Das sortiert die Elemente an Ort und Stelle in aufsteigender Reihenfolge mithilfe von operator<. Um ein Array zu sortieren, übergib arr und arr + n (oder begin(arr) / end(arr)).

Wie sortiert man in C++ in absteigender Reihenfolge?

Übergib eine Vergleichsfunktion, die a > b zurückgibt: sort(v.begin(), v.end(), [](int a, int b){ return a > b; });. Du kannst auch das eingebaute sort(v.begin(), v.end(), greater<int>()); aus <functional> verwenden.

Warum bringt meine Vergleichsfunktion std::sort in C++ zum Absturz?

Deine Vergleichsfunktion muss eine strikte schwache Ordnung (strict weak ordering) sein: Sie muss false zurückgeben, wenn beide Argumente gleich sind. <= oder >= zu verwenden (was bei gleichen Elementen true zurückgibt) verletzt diese Regel und ist undefiniertes Verhalten - std::sort kann außerhalb der Grenzen lesen und abstürzen. Vergleiche immer mit < oder >.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S