

|
![]() |
|
![]() |
void versickern(int a[], int n, int i){ //i=Index des zu versickernden Elementes
int j, h=a[i];
if(i<0) return; //wenn i ungültig, dann abbrechen
while(i<n/2){ //solange noch Söhne vorhanden sind
j=i+i+1; //setze Vergleichselement j auf linken Sohn von i
if(j+1<n && a[j]<a[j+1]) j++; //wenn rechter Sohn größer linkem, dann dieser als j setzen
if(h>=a[j]) break; //wenn Heap-Ordnung hergestellt, dann abbrechen
a[i]=a[j]; i=j; //Element im Heap nach unten versickern
}
a[i]=h; //Element an Endposition einordnen
}
Beim Versickern eines Elementes in einem Binärbaum werden maximal log(n) Vertauschungen durchgeführt, was der Höhe
des Baumes entspricht. Daher ergibt sich auch die Zeitkomplexität von O(log(n)) für diese Methode.void konstruktion(int a[], int n){
int i;
for(i=n/2-1; i>=0; i--)
versickern(a,n,i);
}
Folgendes Beispiel zeigt, wie diese Konstruktion schrittweise erfolgt:| 1.) Ausgangssituation: die Schlüssel liegen durcheinander und fernab von jeder Heapordnung im Binärbaum (Array) vor. | ![]() |
| 2.) Im Array sind n=8 Elemente vorhanden. n/2=4, also ist n/2-1=3 das Element, von welchem aus nach unten hin das Versickern ausgeführt wird. a[3]=8, mit diesem Element als Wurzel ist die Heap-Bedingung des darunter liegenden Teilbaumes erfüllt (8>=6), sodass nicht versickert werden muss. | ![]() |
| 3.) Der Arrayzeiger wird dekrementiert und zeigt nun auf a[2]=1. Hier ist die Heap-Bedingung für den darunter liegenden Teilbaum ganz offensichtlich nicht erfüllt. a[2] hat die zwei Söhne a[5]=4 und a[6]=7. Da a[6]>a[5] ist, wird das aktuelle Element a[2] mit diesem getauscht, "versickert" im Baum. Da anschließend keine Söhne mehr vorhanden sind, bricht der Algorithmus ab und die 1 gilt als versickert. | ![]() |
| 4.) Nach der Dekrementierung des Zeigers wird das Versickern für a[1]=5 ausgeführt, für welches die Heap-Bedingung ebenfalls verletzt ist. Sukzessive wird bei den Söhnen nach dem größeren Element gesucht und mit 5 getauscht. Da 8>2 ist, kommt die 5 zunächst auf deren Platz, um dann noch eine Position weiter über die 6 ans Ende des Arrays zu versickern. | ![]() |
| 5.) Als letztes Element wird a[0]=3 betrachtet, für welches die Heap-Bedingung natürlich auch verletzt ist. Nach dem Finden der größten Söhne gelangt die 3 über a[1]=8, a[3]=6 und a[7]=5 ans Ende des Arrays. | ![]() |
| 6.) Nachdem das erste Arrayelement versickert ist, gilt der Heap als konstruiert. Schön zu sehen, dass man nun auf das größte Arrayelement über a[0] zugreifen kann und dies nur einen einzigen Schritt erfordert. Die Heap-Bedingung ist für alle Unterbäume erfüllt, sodass es sich bei dem dargestellten Binärbaum auch wirklich um einen Heap handelt. | ![]() |
| 1.) Ausgangssituation: der oben konstruierte MaxHeap. | ![]() |
| 2.1) 8 wird an das Ende des Arrays eingefügt, das dortige Element 3 wird dafür als neue Wurzel gesetzt, was ein Versickern entlang des grünen Pfades zur Folge hat. | ![]() |
| 2.2) Nach dem Versickern der 3 steht die 7 als größtes Element auf der Wurzel-Position. | ![]() |
| 3.1) 7 wird vor die bereits fest gelegte Position der 8 eingefügt, mit der dortigen 1 getauscht, welche anschließend versickert. | ![]() |
| 3.2) Nach dem Versickern der 1 steht die 6 auf der Wurzel. | ![]() |
| 4.1) 6 wird ans Ende des Rest-Heaps verschoben, die 3 von dort kommt in die Wurzel und versickert eine Position nach links, bis der Rest-Binärbaum wieder einen Heap darstellt. | ![]() |
| 4.2) Nach dem Versickern der 3 steht das nächstgrößte Element, die 5, in der Wurzel. | ![]() |
| 5.1) 5 wird mit der 2 ans Heap-Ende getauscht, erhält dadurch seinen festen Platz in der sortierten Folge. Die 2 muss nun versickern und bewegt sich dabei eine Position nach rechts, wo das Ende des Rest-Heaps erreicht ist. | ![]() |
| 5.2) Nachdem die 2 versickert ist, steht mit 4 das nächst kleinere Element in der Wurzel. | ![]() |
| 6.1) 4 kommt ans Heap-Ende, wodurch die 1 in die Wurzel gelangt und eine Position nach links versickert, bevor die Ordnung des Rest-Heaps wieder hergestellt ist. | ![]() |
| 6.2) Nach dem Versickern der 1 steht mit der 3 das nächst größere Element zum Einordnen bereit. | ![]() |
| 7.) Die 3 wird eingeordnet, die 2, welche dadurch in die Wurzel kommt, verletzt die Heap-Ordnung nicht, wodurch nicht versickert werden muss. | ![]() |
| 8.) Die 2 kommt ans Ende des aus nur noch 2 Elementen bestehenden Rest-Heaps. Die 1 kommt in die Wurzel. Da der Heap aus nur noch einem Element (der Wurzel) besteht, erfolgt kein Versickern dieser. | ![]() |
| 9.) Die finale Darstellung: alle Elemente wurden sortiert von hinten nach vorn im Array eingefügt, was nun in aufsteigender Reihenfolge vorliegt. Klar, dass dies keinen Heap mehr darstellt - das soll uns aber nicht weiter stören. | ![]() |
void heapsort(int a[], int n){
int i, tmp;
konstruktion(a,n);
for(i=n-1; i>=0; i--){
tmp=a[0]; a[0]=a[i]; a[i]=tmp;
versickern(a,i,0);
}
}