|
|
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); } }