Der Bubble-Sort-Algorithmus

Der Bubble-Sort-Algorithmus

🌐 Read in English
📅 2026-09-11 ✍ Andreas Wittmann đŸ‘ïž ... algorithm sorting computer-graphics

Eine Liste zu sortieren ist eines der ersten Probleme, die jeder Programmierer löst, und Bubble Sort ist meist der erste Algorithmus dafĂŒr. Er ist nicht schnell und außerhalb des Klassenzimmers selten die richtige Wahl — aber er ist kurz, braucht keinen zusĂ€tzlichen Speicher, und sein Verhalten lĂ€sst sich Schritt fĂŒr Schritt gut beobachten, was ihn zu einem guten Einstieg macht, bevor man sich schnellere Algorithmen wie Quicksort oder Merge Sort ansieht.

Die Idee

Bubble Sort durchlĂ€uft die Liste wiederholt und vergleicht jedes Paar benachbarter Elemente. Ist ein Paar falsch angeordnet, werden die beiden vertauscht. Ein vollstĂ€ndiger Durchlauf befördert den grĂ¶ĂŸten verbleibenden Wert bis ganz ans Ende der Liste — wie eine Blase, die aufsteigt — denn jeder Tausch, der ihn betrifft, trĂ€gt ihn eine Position weiter nach rechts.

Nach dem ersten Durchlauf steht fest, dass das grĂ¶ĂŸte Element an seiner finalen Position ist, sodass der nĂ€chste Durchlauf nur noch alles davor betrachten muss. Das fĂŒr n - 1 DurchlĂ€ufe ĂŒber eine Liste mit n Elementen zu wiederholen reicht aus, um sie vollstĂ€ndig zu sortieren, da jeder Durchlauf mindestens ein weiteres Element endgĂŒltig platziert.

Implementierung

#include "plot.hpp"
#include <stdlib.h>

const int N = 20;
int values[N];
int i = 0;
int j = 0;
bool done = false;
int tick = 0;

void setup() {
    PlotCanvas(640, 480);

    for (int k = 0; k < N; k++) {
        values[k] = k + 1;
    }

    srand(1);
    for (int k = N - 1; k > 0; k--) {
        int r = rand() % (k + 1);
        int tmp = values[k];
        values[k] = values[r];
        values[r] = tmp;
    }
}

void frame() {
    // Slow the animation down: one comparison every few frames instead of
    // one per frame, so individual steps stay visible.
    if (!done && ++tick >= 4) {
        tick = 0;

        if (values[j] > values[j + 1]) {
            int tmp = values[j];
            values[j] = values[j + 1];
            values[j + 1] = tmp;
        }

        j++;
        if (j >= N - 1 - i) {
            j = 0;
            i++;
            if (i >= N - 1) {
                done = true;
            }
        }
    }

    PlotBackground(250, 250, 250);

    int barWidth = plot_width / N;
    for (int k = 0; k < N; k++) {
        if (!done && (k == j || k == j + 1)) {
            PlotColor(220, 60, 60);   // pair being compared
        } else if (k >= N - i) {
            PlotColor(70, 170, 90);   // already in final position
        } else {
            PlotColor(30, 100, 200);  // untouched
        }

        int height = values[k] * (plot_height - 20) / N;
        int x0 = k * barWidth + 1;
        int x1 = x0 + barWidth - 2;
        int y0 = plot_height - height;
        int y1 = plot_height;
        PlotFilledRectangle(x0, y0, x1, y1);
    }
}
Open in full editor →

Die beiden verschachtelten Schleifen spiegeln die beiden Ideen von oben: der Ă€ußere Index i zĂ€hlt, wie viele Elemente am Ende der Liste bereits endgĂŒltig platziert sind, und der innere Index j lĂ€uft durch den noch unsortierten Teil und vergleicht Nachbarn. Statt etwas auszugeben, zeichnet frame() das Array als Balken — die Höhe ist der Wert, die Farbe zeigt, was der Algorithmus gerade tut:

Damit einzelne Vergleiche sichtbar bleiben statt sofort durchzulaufen, fĂŒhrt das Sample nur alle paar Aufrufe von frame() einen Vergleich aus (siehe den tick-ZĂ€hler) — alles andere, einschließlich des Mischens des Arrays in setup(), lĂ€uft genau einmal.

Schritt fĂŒr Schritt durchgerechnet

FĂŒr die Liste [5, 1, 4, 2, 8] vergleicht der erste Durchlauf jedes benachbarte Paar von links nach rechts:

Vergleich vorher tauschen? nachher
5, 1 [5, 1, 4, 2, 8] ja [1, 5, 4, 2, 8]
5, 4 [1, 5, 4, 2, 8] ja [1, 4, 5, 2, 8]
5, 2 [1, 4, 5, 2, 8] ja [1, 4, 2, 5, 8]
5, 8 [1, 4, 2, 5, 8] nein [1, 4, 2, 5, 8]

8 — der grĂ¶ĂŸte Wert — ist nach nur einem Durchlauf an seine finale Position geblubbert. Die restlichen DurchlĂ€ufe wiederholen denselben Prozess ĂŒber das schrumpfende unsortierte PrĂ€fix [1, 4, 2, 5], [1, 2, 4] und so weiter, bis nichts mehr zu vergleichen bleibt.

Warum er selten verwendet wird

Jeder Durchlauf kostet O(n), und eine vollstĂ€ndige Sortierung braucht bis zu n - 1 DurchlĂ€ufe, also ist Bubble Sort im schlechtesten und im durchschnittlichen Fall O(nÂČ) — quadratisch schlechter als das O(n log n) von Merge Sort oder Quicksort. Eine gĂ€ngige Optimierung merkt sich, ob wĂ€hrend eines Durchlaufs ĂŒberhaupt getauscht wurde, und bricht frĂŒhzeitig ab, falls nicht — das drĂŒckt den besten Fall (eine bereits sortierte Liste) auf O(n). Die Version hier verzichtet auf diese PrĂŒfung, um Code und Visualisierung einfach zu halten; jeder Lauf macht unabhĂ€ngig von der Eingabe alle n - 1 DurchlĂ€ufe.

Sein heutiger Wert ist fast ausschließlich pĂ€dagogisch: Er ist die einfachste mögliche EinfĂŒhrung in vergleichsbasiertes Sortieren und eine nĂŒtzliche Grundlinie, um schnellere Algorithmen zu vergleichen, die dasselbe Problem mit klĂŒgeren Strategien lösen — die Liste zu teilen statt sie wiederholt zu durchlaufen, wie bei Merge Sort und Quicksort.

Fazit

Bubble Sort verwandelt Sortieren in eine Folge lokaler Entscheidungen: zwei Nachbarn ansehen, sie vertauschen, falls sie falsch herum stehen, und das wiederholen, bis nichts mehr zu korrigieren ist. Diese Einfachheit macht ihn zu einer schlechten Wahl fĂŒr große Listen, aber zu einer guten, um ein GefĂŒhl dafĂŒr zu entwickeln, was ein Sortieralgorithmus tatsĂ€chlich tut — einen Vergleich nach dem anderen.