Der Selection-Sort-Algorithmus
Bubble Sort tauscht bei jedem Durchlauf Nachbarn, und Insertion Sort schiebt Elemente zur Seite, um Platz fĂŒr jeden neuen Wert zu schaffen. Selection Sort verfolgt den direktesten Ansatz der drei: FĂŒr jede Position wird der kleinste verbleibende Wert in der gesamten Liste gesucht und direkt an seine Stelle getauscht.
Die Idee
Wie Insertion Sort hĂ€lt Selection Sort einen sortierten PrĂ€fix am Anfang der Liste. Aber statt jedes neue Element dort einzufĂŒgen, wo es zwischen seinen Nachbarn hingehört, durchsucht es den gesamten unsortierten Rest nach dem Gesamtminimum und platziert dieses Minimum dann mit einem einzigen Tausch direkt am Ende des sortierten PrĂ€fix.
Das macht die beiden ArbeitshĂ€lften im Vergleich zu den anderen beiden Algorithmen asymmetrisch: Das Finden des Minimums braucht einen vollstĂ€ndigen Scan des Rests, aber das Platzieren kostet genau einen Tausch â nie mehr, egal wie weit es von seinem Platz entfernt war.
Implementierung
#include "plot.hpp"
#include <stdlib.h>
const int N = 20;
int values[N];
int i = 0;
int j = 1;
int minIdx = 0;
bool done = false;
int tick = 0;
void setup() {
PlotCanvas(640, 480);
for (int n = 0; n < N; n++) {
values[n] = n + 1;
}
srand(1);
for (int n = N - 1; n > 0; n--) {
int r = rand() % (n + 1);
int tmp = values[n];
values[n] = values[r];
values[r] = tmp;
}
}
void frame() {
// Slow the animation down: one step every few calls to frame() instead
// of one per call, so individual comparisons stay visible.
if (!done && ++tick >= 4) {
tick = 0;
if (j <= N - 1) {
if (values[j] < values[minIdx]) {
minIdx = j;
}
j++;
} else {
if (minIdx != i) {
int tmp = values[i];
values[i] = values[minIdx];
values[minIdx] = tmp;
}
i++;
if (i >= N - 1) {
done = true;
} else {
minIdx = i;
j = i + 1;
}
}
}
PlotBackground(250, 250, 250);
int barWidth = plot_width / N;
for (int idx = 0; idx < N; idx++) {
if (!done && idx == minIdx) {
PlotColor(220, 60, 60); // smallest value found so far this pass
} else if (idx < i) {
PlotColor(70, 170, 90); // sorted prefix
} else {
PlotColor(30, 100, 200); // not yet reached
}
int height = values[idx] * (plot_height - 20) / N;
int x0 = idx * barWidth + 1;
int x1 = x0 + barWidth - 2;
int y0 = plot_height - height;
int y1 = plot_height;
PlotFilledRectangle(x0, y0, x1, y1);
}
}
i markiert das Ende des sortierten PrÀfix, wie beim Insertion-Sort-Sample.
j scannt vorwÀrts durch den unsortierten Rest auf der Suche nach einem
kleineren Wert als dem aktuellen minIdx; sobald j am Ende angekommen
ist, wird der Wert bei minIdx an Position i getauscht und der nÀchste
Durchlauf beginnt. Wie zuvor fĂŒhrt frame() nur alle paar Aufrufe einen
solchen Schritt aus, und nur das laufende Minimum wird hervorgehoben â der
Scan-Zeiger j bewegt sich ohne eigene Farbe durch den blauen Bereich, da
fĂŒr den nĂ€chsten Tausch nur der Minimum-Kandidat zĂ€hlt.
- grĂŒn â der sortierte PrĂ€fix, Werte bereits an ihrer finalen Position
- rot â der bisher kleinste gefundene Wert in diesem Durchlauf
- blau â in diesem Durchlauf noch nicht gescannt
Schritt fĂŒr Schritt durchgerechnet
FĂŒr [5, 1, 4, 2, 8] durchsucht der erste Durchlauf die gesamte Liste
nach dem Minimum:
| j | values[j] | neues Minimum? | minIdx |
|---|---|---|---|
| â | â | Start | 0 (Wert 5) |
| 1 | 1 | ja, 1 < 5 | 1 |
| 2 | 4 | nein, 4 > 1 | 1 |
| 3 | 2 | nein, 2 > 1 | 1 |
| 4 | 8 | nein, 8 > 1 | 1 |
Der Scan endet mit minIdx = 1, also werden values[0] und values[1]
getauscht: [1, 5, 4, 2, 8]. Der nÀchste Durchlauf durchsucht nur noch
[5, 4, 2, 8] nach seinem Minimum (2), tauscht es an Index 1, und so
weiter â jeder Durchlauf verkleinert den unsortierten Rest um eins und
kostet genau einen Tausch.
Warum die Anzahl der Tausche zÀhlt
Selection Sort ist insgesamt O(nÂČ), nicht besser als Bubble oder
Insertion Sort â die Suche nach dem Minimum kostet weiterhin O(n) pro
Durchlauf, bei n DurchlĂ€ufen. Was ihn auszeichnet: Er fĂŒhrt höchstens
n - 1 Tausche insgesamt aus, einen pro Durchlauf, egal wie durcheinander
die Eingabe ist. Bubble Sort und Insertion Sort können jeweils bis zu
O(nÂČ) Tausche oder Verschiebungen bei einer schlecht sortierten Liste
ausfĂŒhren.
Diese Eigenschaft war frĂŒher wichtiger als heute: Auf Hardware, wo ein
Tausch viel teurer ist als ein Vergleich â Schreiben in langsamen Speicher,
oder Flash-Speicher mit begrenzter Anzahl an Schreibzyklen â ist die
begrenzte Schreibanzahl von Selection Sort ein echter Vorteil, selbst bei
gleichen O(nÂČ)-Vergleichskosten. Anders als die anderen beiden ist er
auĂerdem nicht stabil: Ein weit entferntes Minimum an seine Stelle zu
tauschen kann es an gleichwertigen Elementen vorbeibefördern und damit
ihre relative Reihenfolge Àndern.
Fazit
Alle drei einfachen Sortierverfahren leisten O(nÂČ) Arbeit, aber sie
verteilen sie unterschiedlich: Bubble Sort durchsucht und tauscht Nachbarn
wieder und wieder, Insertion Sort schiebt Elemente, um eine LĂŒcke zu
öffnen, und Selection Sort scannt einmal pro Durchlauf und committet sich
auf einen einzigen Tausch. Die drei nebeneinander zu vergleichen zeigt
gut, dass "gleiche asymptotische KomplexitÀt" nicht "gleicher Algorithmus"
bedeutet â die Konstanten, das Zugriffsmuster und Eigenschaften wie
StabilitÀt hÀngen weiterhin davon ab, welchen man wÀhlt.