Home > Informatik > Einführung in die OOP > 5. Sortieren und Suchen > 5.6 Ein Bucketsort

5.6 Ein Bucketsort

Rekapitulation Distributionsort

Bereits in der Folge 2 hatten wir einen sehr einfachen Sortieralgorithmus besprochen, den Distributionsort. Die Grundidee dieses Verfahrens besteht darin, jedes Element gemäß seinem Wert in eine passende Schublade einzuordnen und anschließend die Schubladen in der richtigen Reihenfolge auszulesen. Beim Distributionsort hatten wir für jede Zahl, die in dem Array vorkommt, eine eigene Schublade erzeugt. Kommen in einem aus 1000 Elementen bestehendem Array 214 verschiedene Zahlen vor, benötigen wir für den Distributionsort genau 214 Schubladen bzw. einen Verteilungsarray mit 214 Elementen. Für jedes Vorkommen der Zahl i wird dann das Arrayelement verteilung[i] um 1 inkrementiert.

Der Bucketsort-Algorithmus

Auch das Verfahren, das wir jetzt besprechen, ist noch eine relativ einfache Variante des Bucketsorts. Aber immerhin ein gewaltiger Fortschritt gegenüber dem simplen Distributionsort.

Vor dem ersten Sortierschritt
Autor: Ulrich Helmich 11/2025, Lizenz: Public domain

Dieses Bild zeigt einen kleinen Array mit Zahlen im Bereich 6 bis 57. In der unteren Reihe sehen wir sechs Eimer, engl. Buckets, die Arrayelemente aufnehmen können. Der erste Eimer ist für die Zahlen 0 bis 9 zuständig, der zweite für die Zahlen 10 bis 19 und so weiter. Der letzte Eimer schließlich kann die Zahlen 50 bis 59 aufnehmen.

Verteilung der ersten sechs Zahlen des Arrays
Autor: Ulrich Helmich 11/2025, Lizenz: Public domain

Der Array wurde hier schon bis zum sechsten Element (Index 5) durchlaufen. Die Zahlen, die in den Arrayelementen stehen, wurden in den jeweils passenden Eimer einsortiert. Zur Zeit enthält jeder Eimer nur eine Zahl. Aber wir sind ja auch noch nicht fertig.

Verteilung aller Zahlen
Autor: Ulrich Helmich 11/2025, Lizenz: Public domain

Der Arraydurchlauf ist jetzt beendet, und alle im Array vorkommenden Zahlen wurden in die Eimer einsortiert - und zwar nicht aufsteigend oder absteigend, sondern in der Reihenfolge, wie sie im Array vorgekommen sind.

Sortieren der Zahlen in den Buckets
Autor: Ulrich Helmich 11/2025, Lizenz: Public domain

Jetzt werden die Zahlen in jedem Bucket sortiert. Dazu kann durchaus ein einfacher Sortieralgorithmus wie Bubblesort oder Insertionsort benutzt werden. Eine grundsätzliche Alternative wäre aber auch möglich, nämlich die Zahlen bereits beim Einfügen in die Buckets korrekt einzusortieren, ähnlich wie im Insertionsort.

Zurückkopieren der Zahlen in den Buckets
Autor: Ulrich Helmich 11/2025, Lizenz: Public domain

Jetzt werden die sortierten Zahlen aus den Buckets wieder in den eigentlichen Array kopiert. Auf dem Bild sieht man die Situation, nachdem der zweite Eimer "geleert" wurde.

Der Bucketsort ist abgeschlossen
Autor: Ulrich Helmich 11/2025, Lizenz: Public domain

Hier schließlich sehen wir die Situation, nachdem auch der letzte Eimer geleert wurde.

Der Quelltext

Konstanten

public class Eimer
{
    private final int MAX = 12;      // Anzahl Elemente im Hauptarray
    private final int MAXWERT = 57;  // Wertebereich: 1..MAXWERT

    private int[] zahlen;

Hier sehen wir den Kopf der Klasse Eimer. Die Anzahl der Arrayelemente wurde hier auf MAX = 12 festgelegt, und der höchste vorkommende Wert ist MAXWERT = 57. Damit bezieht sich der Quelltext direkt auf unser Beispiel mit den sechs Eimern. Der zu sortierende Array wurde als Instanzvariable zahlen[] festgelegt.

Konstruktor

Betrachten wir nun den Konstruktor der Klasse Eimer:

    public Eimer()
    {
        zahlen = ArrayTools.erzeugeArray(MAX);
        ArrayTools.fuelleArrayMitZufallszahlen(zahlen, MAX, 1, MAXWERT);

        System.out.println("Anzeigen des unsortierten Arrays");
        ArrayTools.zeigeArray(zahlen, MAX);

        bucketSortMitListe();
        

        System.out.println("\nAnzeigen des sortierten Arrays");
        ArrayTools.zeigeArray(zahlen, MAX);        
    }

Mit der Methode erzeugeArray() der Klasse ArrayTools wird nun ein int-Array mit 12 Zahlen deklariert und initialisiert. Die Zuweisung der Werte findet dann mithilfe der Methode

ArrayTools.fuelleArrayMitZufallszahlen(zahlen, MAX, 1, MAXWERT);

statt. Dieser Methode werden der zu füllende Array, die Anzahl der zu erzeugenden Elemente, der kleinste Wert und der größte Wert als Parameter übergeben.

Dann wird - zur Kontrolle - der erzeugte Array in der Konsole ausgegeben, mithilfe der Methode ArrayTools.zeigeArray().

Jetzt wird die eigentliche Bucketsort-Methode bucketSortMitListe() aufgerufen, und anschließend wird - wieder zur Kontrolle - der (hoffentlich) sortierte Array in der Konsole ausgegeben.

Die Methode bucketSortMitListe()

Kopf und Konstanten

Betrachten wir zunächst den Kopf der Methode und eine Konstante:

public void bucketSortMitListe()
{
    final int BUCKETS = (MAXWERT / 10) + 1;

Die Anzahl der Eimer wird hier aus dem höchsten Wert der Zahlen MAXWERT und der Eimerbreite B = 10 berechnet. Unter der Eimerbreite (bucket width) versteht man den Wertebereich, der innerhalb eines Eimers gespeichert werden soll. In unserem Beispiel waren das jeweils 10 Zahlen, zum Beispiel im ersten Eimer die Zahlen 0..9, im zweiten Eimer die Zahlen 10..19 und so weiter.

Anlegen der Buckets

    Liste[] bucket = new Liste[BUCKETS];
    for (int i = 0; i < BUCKETS; i++)
        bucket[i] = new Liste();

Diese drei Zeilen sind nicht ohne Erläuterung verständlich. Wir müssen uns zunächst die Klasse Liste näher anschauen.

Die Klasse Liste

public class Liste
{
    int[] element;
    int   anzahl;
    
    public Liste()
    {
        element = new int[100];
        anzahl  = 0;
    }
    
    public void fuegeEin(int neu)
    {
        if (anzahl >= 100)
            return;

        // 1. Einfügeposition ermitteln (erste Position, an der neu <= element[i] ist)
        int pos = 0;
        while (pos < anzahl && element[pos] < neu)
            pos++;

        // 2. Alle Elemente rechts von pos um eine Position nach rechts verschieben
        for (int i = anzahl; i > pos; i--)
            element[i] = element[i - 1];

        // 3. Neues Element einfügen
        element[pos] = neu;

        // 4. Anzahl erhöhen
        anzahl++;
    }
    
    public void zeigeAn()
    {
       ArrayTools.zeigeArray(element,anzahl);
    }
}

Diese Klasse stellt im Prinzip ein int-Array zur Verfügung, das beim Erzeugen noch keine Elemente besitzt (anzahl = 0). Entscheidend ist die Methode fuegeEin(int neu). Diese Methode hängt nämlich nicht nur einfach eine neue Zahl an das Array an, sondern sortiert die neue Zahl sofort korrekt in das Array ein. Die erste Schleife (while) ermittelt die Einfügeposition, die zweite Schleife (for) verschiebt alle Elemente rechts der Einfügeposition um eine Stelle weiter nach rechts. Dann erst kann das neue Element eingefügt werden. Als viertes wird dann die Instanzvariable anzahl inkrementiert.

Die zeigeAn()-Methode am Ende dient nur zu Kontrollzwecken.

Welchen Sinn hat die Klasse Liste?

Wenn Sie das Beispiel am Anfang dieser Seite aufmerksam studiert haben, wird Ihnen aufgefallen sein, dass die Zahlen des Arrays zunächst völlig ungeordnet in die Eimer "fallen". Wenn das ganze Array abgearbeitet wurde, müssen die Zahlen in den sechs Eimern sortiert werden.

Das kann mithilfe eines einfachen Sortieralgorithmus wie Insertionsort geschehen. Alternativ kann man aber auch dafür sorgen, dass die Zahlen sofort korrekt in die Eimer einsortiert werden. Genau diese Aufgabe übernimmt hier die Klasse Liste bzw. die Methode fuegeEin().

Kommen wir zurück zu unserem Ausgangspunkt, dem Anlegen der Buckets. Betrachten wir jetzt noch einmal die ersten Zeilen der Methode bucketSortMitListe().

Anlegen der Buckets, Fortsetzung

    Liste[] bucket = new Liste[BUCKETS];
    for (int i = 0; i < BUCKETS; i++)
        bucket[i] = new Liste();

Hier wird ein Objekt-Array mit Objekten der Klasse Liste angelegt. Jeder Eimer ist jetzt eine Liste. Das heißt, die Zahlen, die in die Eimer "fallen", werden sofort an Ort und Stelle korrekt einsortiert, jeder Eimer enthält dann ein sortiertes Array von Zahlen. Die Konstante BUCKETS repräsentiert die Anzahl der Eimer. In unserem Quelltext hatten wir diese Konstante auf den Wert 6 gesetzt. Also besitzt das Array bucket jetzt sechs Objekte der Klasse Liste.

Zahlen in die Buckets einfügen

    for (int i = 0; i < MAX; i++)
    {
        int z = zahlen[i];
        int b = z / 10;
        bucket[b].fuegeEin(z);  
    }

Die for-Schleife durchläuft das gesamte Zahlen-Array. Der jeweils gefundene Wert wird in der lokalen Variablen z gespeichert. Der Index b des passenden Buckets wird durch die Formel b = z / 10 berechnet.

Angenommen, die gefundene Zahl ist 38. Die Ganzahl-Division 38/10 liefert den Wert 3. Das heißt, die 38 wird in das bucket-Objekt mit dem Index 3 einsortiert, also in den vierten Eimer. Hier noch einmal das passende Bild aus unserem Beispiel.

Die Zahl 38 befindet sich im vierten Eimer mit dem Index 3
Autor: Ulrich Helmich 11/2025, Lizenz: Public domain

Buckets anzeigen

Dieser Abschnitt der Methode dient nur zu Kontrollzwecken und könnte auch komplett entfallen.

    for (int b = 0; b < BUCKETS; b++)
    {
        System.out.println("Bucket " + b + ":");
        bucket[b].zeigeAn();
        System.out.println();
    }

Buckets in das Hauptarray zurückschreiben

    for (int b = 0; b < BUCKETS; b++)
    {
        for (int i = 0; i < bucket[b].anzahl; i++)
        {
            zahlen[index] = bucket[b].element[i];
            index++;
        }
    }

Beginnend mit dem ersten Bucket (Index b = 0) werden die sortierten Zahlen eines jeden Eimers in das Hauptarray zurückgeschrieben. Die in dem Hauptarray stehenden (alten) Zahlen werden dabei einfach überschrieben - es muss also kein neues Array angelegt werden, sondern das Array wird an Ort und Stelle sortiert.

Achten Sie darauf, dass hier direkt auf die Arrayelemente der einzelnen Bucket-Objekte zugegriffen werden kann. In der Klasse Liste hatten wir den Array element[] sowie die Instanzvariable anzahl nämlich nicht als private deklariert. Daher sind die beiden Instanzvariablen für andere Klassen öffentlich zugänglich. Eine Getter-Methode getElement(int i) ist hier also nicht nötig.

Die Konsolen-Ausgabe

Lassen wir das Programm nun laufen und schauen uns die Konsolen-Ausgabe an:

Anzeigen des unsortierten Arrays
   23   34    5    9   13    6   22   18   57   47
   45    2
Bucket 0:
    2    5    6    9

Bucket 1:
   13   18

Bucket 2:
   22   23

Bucket 3:
   34

Bucket 4:
   45   47

Bucket 5:
   57


Anzeigen des sortierten Arrays
    2    5    6    9   13   18   22   23   34   45
   47   57

Dieses kleine Beispiel zeigt, dass der Bucketsort gut funktioniert. Die sechs Buckets sie wie erwartet mit den passenden Zahlen gefüllt, und man kann auch sehen, dass die Zahlen innerhalb der Buckets sortiert sind.

Experimente mit größeren Arrays

Wir setzen jetzt die beiden Konstanten am Anfang der Klasse Eimer mal auf größere Werte:

    private final int MAX = 100;      // Anzahl Elemente im Hauptarray
    private final int MAXWERT = 200;  // Wertebereich: 1..MAXWERT

Wir wollen also ein Array mit 100 Zahlen im Bereich 0..200 sortieren. Schauen wir uns nun die gekürzte Konsolen-Ausgabe an.

Das unsortierte Array:

   66   80  189   80  160  103   17   26   21   10
   17   70  197   49  149  182   81  172  176   23
   83  116  184  116  162   45   84   63  190   36
   87  124  117   43  113   66  164    6   28  199
  113   97  115   79  141   88  119   24  191  138
  139   60    3  179    7   36  146   21   27   86
  139   50   25  195  103   73   36  192    2   34
   48  158   94  152  167   32  137  114   42  161
  150   61  129   71  169   58   65   80  100   63
  188   95   63   37   15  107  172  134   92  116

Die Buckets 0 bis 5:

Bucket 0:
    2    3    6    7

Bucket 1:
   10   15   17   17

Bucket 2:
   21   21   23   24   25   26   27   28

Bucket 3:
   32   34   36   36   36   37

Bucket 4:
   42   43   45   48   49

Bucket 5:
   50   58

Insgesamt haben wir hier 21 Buckets vorliegen, der letzte Eimer enthält allerdings keine Daten.

Das sortierte Array:

    2    3    6    7   10   15   17   17   21   21
   23   24   25   26   27   28   32   34   36   36
   36   37   42   43   45   48   49   50   58   60
   61   63   63   63   65   66   66   70   71   73
   79   80   80   80   81   83   84   86   87   88
   92   94   95   97  100  103  103  107  113  113
  114  115  116  116  116  117  119  124  129  134
  137  138  139  139  141  146  149  150  152  158
  160  161  162  164  167  169  172  172  176  179
  182  184  188  189  190  191  192  195  197  199

Damit schließen wir unsere Betrachtungen zum Thema "Bucketsort" erst einmal ab. Für Sie gibt es aber noch zwei Aufgaben.

Aufgaben

Übung 5.6 #1

Kopieren Sie sich die Quelltexte der Klassen Eimer und Liste sowie der Klasse ArrayTools in ein neues Java-Projekt und bringen Sie das Ganze zum Laufen.

Testen Sie dann das Programm, indem Sie die Konstanten MAX und MAXWERT verändern.

Übung 5.6 #2

Der interne Array der Klasse Liste ist völlig ungeschützt, da er nicht als private deklariert wurde. Schützen Sie die beiden Instanzvariablen und ergänzen Sie die Klasse Liste dann um entsprechende Getter-Methoden. Die Klasse Eimer müssen Sie dann natürlich auch entsprechend anpassen.

Aufgabe 5.6 #3

Recherchieren Sie im Internet oder überlegen Sie selbst, welches Zeitverhalten den Bucketsort auszeichnet. Liegt hier O(n2) vor oder O(n) oder ein anderes Zeitverhalten?

Begründen Sie auch, warum der Bucketsort dieses Zeitverhalten besitzt.

Seitenanfang
Weiter mit dem Mergesort ...