Datenstrukturen Sortieralgorithmen am Beispiel Java c Y. Pfeifer (Mai 013)
1 Sortieralgorithmen 1.1 Straight Insertion Bei diesem Einfügeverfahren wird eine Zahlenreihe mit n Elementen von links nach rechts durchlaufen, beginnend mit dem. Element. Für jedes Element ki, dessen Inhalt zunächst in einer Hilfsvariable (hier temp) gesichert wird, erfolgt eine wertmäßige Prüfung in Bezug auf die links stehenden Elemente. Alle Elemente, die links von ki stehen und größer als ki sind, werden um einen Platz nach rechts verschoben. Der Inhalt von ki wandert auf den frei gewordenen Platz. Hinweis: Die Elemente werden in einem Array ab dem Index 1 abgespeichert; Arr[0] dient für Verwaltungszwecke! Anzahl der Vergleiche: [n-1; n/+n/-1] Im günstigsten Fall (Best Case) liegt eine sortierte Folge vor (k1<k<...<kn), sodass ein Schlüssel immer größer ist als die von ihm links stehenden. Die for-schleife wird 1x für alle Elemente durchlaufen, die Bedingung für die innere while-schleife trifft nie zu. Beim Worst Case handelt es sich um eine umgekehrt sortierte Folge (k1>k>...>kn), für jedes Element fällt das Maximum an Vergleichen an (jeweils Anzahl der links stehenden Elemente + 1 = 0 +1 + +3 +... + (n-1) + (n-1)). 1 public static int[] straightinsert(int arr[],int anzelemente,boolean comment){ 3 4 int temp,i,j; 5 int dl=0; 6 int n=anzelemente; 7 int anzvgl=0; 8 9 for(i=;i<=n;i++) 10 { 11 temp=arr[i]; // Hilfselement 1 arr[0]=temp; / damit die while Schleife in jedem Fall abbricht / 13 j=i 1; 14 while(arr[j]>temp){ 15 arr[j+1]=arr[j]; j ; 16 anzvgl++; 17 } 18 arr[j+1]=temp; 19 0 dl++; 1 if(comment) ausgabe(arr,dl+". Einfügeschritt"); } 3 4 anzvgl+=n 1; 5 return arr; 6 } Listing 1.1: Straightinsert
1. Shellsort Beim Shellsort, einem weiteren Einfügeverfahren, wird eine Zahlenreihe mit Hilfe der sogenannten l-sortierung vorsortiert. l bezeichnet hierbei die Nummer des x-ten Elements. Beispielsweise wird bei der 3-Sortierung eine Zahlenreihe aus den Elementen, deren Index ein Vielfaches von 3 darstellt, gebildet und diese entsprechend sortiert. Mit den Elementen, die durch Index mod 3 derselben Klasse angehören, wird identisch verfahren. D.h. arr[1+l] wird mit arr[1] verglichen, arr[+l] mit arr[],... l sollte logischerweise n/ nicht überschreiten und wird programmtechnisch auf die größte ganze Zahl festgelegt, welche sich aus n* alpha (alpha = 0,45454) errechnet. Ergibt sich im Laufe der Programmausführung für l der Wert 0, so wird dieser durch 1 ersetzt. l=1 bildet immer den letzten Durchlauf, sodass eine vollständige Sortierung gewährleistet ist. L-Sort: 1 public static int[] lsort(int arr[],int l,int anzelemente,boolean comment){ 3 int temp,i,j; 4 int n=anzelemente; 5 6 for(i=1+l;i<=n;i++) 7 { 8 temp=arr[i]; 9 j=i l; 10 while((j>=1)&&(arr[j]>temp)){ 11 arr[j+l]=arr[j]; 1 j =l; 13 14 //anzvgl++; 15 } 16 arr[j+l]=temp; 17 18 } 19 if(comment) ausgabe(arr," "+l+" Sortierung"); 0 1 return arr; } Shellsort: Listing 1.: L-Sortierung 1 public static int[] shellsort(int arr[], int anzelemente, boolean comment){ 3 double alpha=0.45454; 4 int l=(int)(anzelemente alpha); 5 int n=anzelemente; 6 7 while(l>1){ 8 lsort(arr,l,anzelemente,comment); 9 l=(int)(l alpha); 10 } 11 lsort(arr,1,n,comment); 1 13 return arr; 14 } Listing 1.3: Shellsort 3
1.3 Bubblesort Bubblesort gehört zu den Austauschverfahren und ist durch seine hohe Anzahl an Vergleichen extrem langsam. Beim Durchlauf einer Zahlenreihe von links nach rechts werden jeweils zwei aufeinanderfolgende Zahlen auf Inversion geprüft. Eine Inversion liegt dann vor, wenn eine benachbarte Zahl mit höherem Index einen geringeren Wert aufweist als der Vorgänger. Im Falle einer Inversion findet ein Tausch der Nachbarn statt. Der Durchlauf durch die Zahlenreihe erfolgt solange bis keine Inversion mehr vorliegt. Da sich der größte Zahlenwert einer Reihe innerhalb eines Durchlaufs auf den letzten Platz weiterschiebt, d.h. spätestens nach dem i-ten Durchlauf sitzt das i.-größte Element an der richtigen Stelle, kann der Durchlauf entsprechend verkürzt werden. Durch Speicherung des Indexes des zuletzt vertauschten Elements ist ein Durchlauf nur bis zu diesem Element erforderlich, da alle folgenden Elemente bereits die richtige Reihenfolge aufweisen. Anzahl der Vergleiche im Best Case (sortierte Folge): n-1 Anzahl der Vergleiche im Worst Case: n(n-1)/ = n/-n/ 1 public static int[] bubblesort(int arr[],int anzelemente,boolean comment){ 3 int i,j,r; 4 int temp; 5 int dl=0; 6 7 8 r=anzelemente; // 1. DL geht bis zum Ende 9 while(r>1){ 10 j=0; 11 for(i=1;i<r;i++) 1 { 13 if(arr[i]>arr[i+1]){ // dann vertauschen 14 15 temp=arr[i+1]; 16 arr[i+1]=arr[i]; 17 arr[i]=temp; 18 j=i; // Index merken 19 } 0 } 1 r=j; // letzte Vertauschung in diesem DL dl++; 3 4 if(comment) ausgabe(arr," "+dl+". Durchlauf"); 5 } 6 return arr; 7 } Listing 1.4: Bubblesort 4
1.4 Quicksort zählt ebenfalls zu den Austauschverfahren und ist der schnellste Sortieralgorithmus im Mittel. Vorgehen: man wähle zunächst ein mittleres Element k (Index = größte ganze Zahl, die sich aus den gemittelten Indices von erstem und letztem Element ergibt), dessen Wert für Vergleiche herangezogen wird. durchlaufe die Zahlenreihe von links nach rechts bis ein Wert größer/gleich dem Vergleichswert gefunden wird. Analog dazu: durchlaufe die Zahlenreihe von rechts nach links bis ein Wert kleiner/gleich dem Vergleichswert gefunden wird. Diese heißen Fehlstände bzgl. k. Die Fehlstände werden vertauscht, die Laufindices um 1 erhöht bzw. erniedrigt. Danach wird entsprechend weiter verfahren bis die Durchsuchungen von links und rechts zusammentreffen. Ergebnis des Zerlegungsschritts ist eine Zwei- oder Dreiteilung. Bei einer Zweiteilung kann x in linker oder rechter Teilfolge stehen. Im optimalen Fall steht x an der richtigen Stelle und wird nicht mehr betrachtet. Dann ergibt sich eine Dreiteilung (1. Bereich Werte<x,. Bereich enthält nur x, 3. Bereich Werte>x). Für die sich ergebenden Bereiche wird die Funktion rekursiv wieder aufgerufen, wobei 1- elementige Teilfolgen nicht mehr bearbeitet werden. Das Ende des Algorithmus ist erreicht, wenn nur noch 1-elementige Folgen vorliegen, dann ist die Zahlenreihe vollständig sortiert. 1 public static int []quicksort(int arr[],int l,int r, boolean comment){ 3 int i,j; 4 int temp; 5 int x; // Vergleichselement 6 if(l>=r) return arr; // nichts zu tun 7 i=l; 8 j=r; 9 10 x=arr[(l+r)/]; // Beginn des Zerlegungsschritts 11 System.out.println("Beginn des Zerlegungschritts: x=arr["+(1+r)/+"]="+x); 1 while(i<=j){ 13 while(arr[i]<x) i++; 14 if(comment) System.out.println(" i="+i); 15 while(arr[j]>x) j ; 16 if(comment) System.out.println(" j="+j); 17 if(i<=j){ 18 temp=arr[i]; arr[i]=arr[j]; arr[j]=temp; 19 System.out.println("vertausche i="+i+" und j="+j); 0 i++; j ; 1 // Ende des Zerlegungsschritts 3 ausgabe(arr,""); 4 } 5 } 6 7 quicksort(arr,l,j,comment); 8 quicksort(arr,i,r,comment); 9 30 return arr; 31 } Listing 1.5: Quicksort 5
1.5 Straight Selection Bei dem auch als Direktes Auswählen bezeichneten Verfahren wird in jedem Durchlauf der kleinste Wert einer Zahlenreihe gefunden und mit dem n-ten Platz vertauscht. n beginnt mit Index 1 und endet mit n-1. Danach ist die Zahlenreihe vollständig sortiert. Straight Selection ist im Mittel schneller als Straight Insertion, bei sortierten Folgen ist es umgekehrt, da Straight Insertion im optimalen Fall nur n-1 Vergleiche benötigt. 1 public static int[] straightselect(int arr[],int anzelemente,boolean comment){ 3 int temp,i,j,k,l; 4 int n=anzelemente; 5 6 for(i=1;i<=n 1;i++) 7 { 8 l=i; 9 k=arr[i]; 10 for(j=i+1;j<=n;j++) // suche Element mit minimalem Wert 11 { 1 if(arr[j]<k){ 13 l=j; 14 k=arr[j]; 15 } 16 } 17 temp=arr[i]; arr[i]=arr[l]; arr[l]=temp; // Vertauschung 18 if(comment) ausgabe(arr,""); 19 0 } 1 return arr; } Listing 1.6: Straight Selection 6