Algoritmer och Datastrukturer Kary FRÄMLING Kap. 9, Sid 1 C-språket 2/Kary Främling v2000 och Göran Pulkkis v2003 SORTERING OCH SÖKNING Sortering är ett av de bästa exemplen på problem där valet av lösningsalgoritm radikalt kan ändra på exekveringstiden. Exempel på sorteringalgoritmer, ordnade från den långsammaste till den snabbaste är (den intresserade läsaren kan lätt hitta beskrivningar av dem i flera olika böcker): Insertion sort Shell sort Heapsort Mergesort Quicksort Det är relativt enkelt att programmera en sorteringsalgoritm som fungerar, men vars sorteringstid växer exponentiellt då datamängden att sortera växer. Quicksort-algoritmen är den som används oftast eftersom den för de flesta problem är den snabbaste. Dess exekveringstid växer i medeltal i proportionen n*log(n), d.v.s att exekveringstiden hålls inom rimliga gränser även med mycket stora mängder data. Detta betyder att Quicksortalgoritmens komplexitet är O(n*log(n)), då n betecknar antalet tabellelement. Exempel 1 visar en implementering av quicksort, som är relativt lätt att skriva om och generalisera för vilken sorts data som helst. Exempel 1. Implementering av Quicksort (kompilerat och kört). Se boken WEISS: Data Structures and Algorithm Analysis in C. #include <stdio.h> #define MIN_DATA 0 #define CUTOFF 10 typedef int InputType; void quicksort(inputtype a[], unsigned int n); void q_sort(inputtype a[], int left, int right); InputType median3(inputtype a[], int left, int right); void insertion_sort(inputtype a[], unsigned int n); void swap(inputtype *a, InputType *b);
Algoritmer och Datastrukturer Kary FRÄMLING Kap. 9, Sid 2 main() int i; InputType itab[] = 0, 34, 8, 64, 51, 32, 21, 55, 88, 9, 11, 33, 54, 72, 25, 39, 21, 57, 93, 6, 17, 22, 64, 75, 83, 31, 19, 64, 98, 5, 13, 57, 51, 71, 21, 33, 85, 91, 37, 7, 18 ; printf("osorterad tabell\n"); for ( i = 1 ; i <= 40 ; i++ ) if ( i%10 == 0 ) printf("\n"); quicksort(itab, 40); printf("sorterad tabell\n"); for ( i = 1 ; i <= 40 ; i++ ) if ( i%10 == 0 ) printf("\n"); void quicksort(inputtype a[], unsigned int n) q_sort(a, 1, n); insertion_sort(a, n); void q_sort(inputtype a[], int left, int right) int i, j; InputType pivot; if ( left + CUTOFF <= right ) pivot = median3(a, left, right); i = left; j = right - 1; for ( ;; ) while ( a[++i] < pivot ); while ( a[--j] > pivot ); if ( i < j ) swap(&a[i], &a[j]); else break; swap(&a[i], &a[right - 1]); /* restore pivot */ q_sort(a, left, i - 1); q_sort(a, i + 1, right); /* Return median of left, center and right. */ /* Order these and hide pivot */
Algoritmer och Datastrukturer Kary FRÄMLING Kap. 9, Sid 3 InputType median3(inputtype a[], int left, int right) int center; center = (left + right)/2; if ( a[left] > a[center] ) swap(&a[left], &a[center]); if ( a[left] > a[right] ) swap(&a[left], &a[right]); if ( a[center] > a[right] ) swap(&a[center], &a[right]); /* invariant: a[left] <= a[center] <= a[right] */ swap(&a[center], &a[right - 1]); /* hide pivot */ return a[right - 1]; /* return pivot */ void insertion_sort(inputtype a[], unsigned int n) unsigned int j, p; InputType tmp; a[0] = MIN_DATA; /* sentinel */ for ( p = 2 ; p <= n ; p++ ) tmp = a[p]; for ( j = p ; tmp < a[j - 1] ; j-- ) a[j] = a[j - 1]; a[j] = tmp; void swap(inputtype *a, InputType *b) InputType tmp; tmp = *a; *a = *b; *b = tmp; Programmet körs: goran@xena:~/cprog2$ quick1 Osorterad tabell 34 8 64 51 32 21 55 88 9 11 33 54 72 25 39 21 57 93 6 17 22 64 75 83 31 19 64 98 5 13 57 51 71 21 33 85 91 37 7 18 Sorterad tabell 5 6 7 8 9 11 13 17 18 19 21 21 21 22 25 31 32 33 33 34 37 39 51 51 54 55 57 57 64 64 64 71 72 75 83 85 88 91 93 98
Algoritmer och Datastrukturer Kary FRÄMLING Kap. 9, Sid 4 I Exempel 1 sorteras en tabell av heltal, men genom att ändra på datatypen InputType skulle vilken typ av data som helst kunna sorteras. Quicksort är en algoritm som delar upp tabellen som skall sorteras i mindre tabeller som sorteras rekursivt. Precis som i kapitlet om binära träd är det ytterst viktigt att välja rätt mittenvärde (pivot) fôr att sorteringen skall vara snabb. Detta utförs av funktionen median3, som dessutom gör en liten delsortering som är viktig för algoritmens hastighet. I det här fallet utförs sorteringen inte till slut med quicksort, utan den slutgiltiga sortering görs genom insertion sort som är mycket snabbt för nästan sorterad data. Enligt test som utförts är detta ca. 15% snabbare än att utföra sorteringen helt till slut med quicksort. Konstanten CUTOFF bestämmer hur långt sorteringen görs med quicksort. I Exempel 1 har tabellens första element reserverats för algoritmens behov, varför datan att sortera börjar först från index 1. Eftersom quicksort så tydligt är den snabbaste sorteringsalgoritmen för de flesta problem, finns det i C inkluderat en standardfunktion qsort som implementerar den. Exempel 2 visar hur den kan användas för att sortera samma heltalstabell som i det förra exemplet.
Algoritmer och Datastrukturer Kary FRÄMLING Kap. 9, Sid 5 Exempel 2. Användning av standardfunktionerna qsort och bsearch (kompilerat och kört) #include <stdio.h> #include <stdlib.h> #include <string.h> #define NELEMS(arr) (sizeof(arr) / sizeof(arr[0])) typedef int InputType; int sortfunction( const void *a, const void *b); main() int i, index; InputType itab[] = 34, 8, 64, 51, 32, 21, 55, 88, 9, 11, 33, 54, 72, 25, 39, 21, 57, 93, 6, 17, 22, 64, 75, 83, 31, 19, 64, 98, 5, 13, 57, 51, 71, 21, 33, 85, 91, 37, 7, 18 ; InputType *itemptr, key; printf("osorterad tabell\n"); for ( i = 0 ; i < NELEMS(itab) ; i++) if ( (i + 1)%10 == 0 ) printf("\n"); printf("\n"); qsort((void *)itab, NELEMS(itab), sizeof(inputtype), sortfunction); printf("sorterad tabell\n"); for ( i = 0 ; i < NELEMS(itab) ; i++) if ( (i + 1)%10 == 0 ) printf("\n"); printf("\n"); key = 31; itemptr = (InputType*) bsearch (&key, itab, NELEMS(itab), sizeof(inputtype), sortfunction); if ( itemptr!= NULL ) index=itemptr-itab; printf("number %d found at index %d\n",key,index); else printf("number %d not found!\n", key); int sortfunction(const void *a, const void *b) if ( *((InputType*)a) > *((InputType*)b) ) return 1; if ( *((InputType*)a) < *((InputType*)b) ) return -1; return 0;
Algoritmer och Datastrukturer Kary FRÄMLING Kap. 9, Sid 6 Programmet körs: goran@xena:~/cprog2$ quick2 Osorterad tabell 34 8 64 51 32 21 55 88 9 11 33 54 72 25 39 21 57 93 6 17 22 64 75 83 31 19 64 98 5 13 57 51 71 21 33 85 91 37 7 18 Sorterad tabell 5 6 7 8 9 11 13 17 18 19 21 21 21 22 25 31 32 33 33 34 37 39 51 51 54 55 57 57 64 64 64 71 72 75 83 85 88 91 93 98 Number 31 found at index 15 Det finns även en standardfunktion för att snabbt granska om ett givet element är inkluderat i en sorterad tabell. Funktionen heter bsearch och utför en snabb binär sökning i tabellen.