WYKŁAD 9 Algorytmy sortowania elementów zbioru (tablic) Programy: c4_1.c... c4_3.c Tomasz Zieliński
/* Przyklad 4.1 - SORTOWANIE TABLIC - metoda najprostsza */ #include <stdio.h> #define ROZMIAR 11 void sortuj( int dane[ ], int N ); // funkcja sortująca int minim( int dane[ ], int start, int N ); // funkcja znajdowania min /* program glowny ---------------------------------------------------------------- */ void main() int tab[ ROZMIAR ] = 0, 9, 2, 7, 4, 5, 6, 3, 8, 1, 10 ; int i; sortuj( tab, ROZMIAR ); /* uporzadkuj dane ------------------- */ for( i=0; i < ROZMIAR; i++) /* wydrukuj wynik sprzatania -------*/ printf(" %d \n", tab[ i ] ); /* funkcje pomocnicze ----------------------------------------------------------- */ void sortuj( int dane[ ], int N ) int x, i, j; for( i=0; i < N-1; i++ ) // powtarzaj dla i = 0, 1, 2,..., N-2 // znajdz indeks j próbki o min wartości, j = minim( dane, i+1, N ); // leżącej powyżej dane[i] if ( dane[ j ] < dane[ i ]) // zamien miejscami dane[i] i dane[j], // jesli dane[ j ] < dane[ i ] x = dane[ i ]; dane[ i ] = dane[ j ]; dane[ j ] = x;
/* ---------------------------------------------------------------------------------------- */ /* znajdz indeks probki, i = start,..., N */ /* dla ktorej wartosc dane[ i ] jest najmniejsza */ /* ---------------------------------------------------------------------------------------- */ int minim( int dane[ ], int start, int N ) int i, imin, vmin; imin = start; vmin = dane[ start ]; // indeks minimum = start // wartość minimum = dane[start] for( i = start+1; i < N; i++ ) if (vmin > dane[i] ) imin=i; vmin = dane[ i ]; // kolejny element tablicy // jeśli mniejsze od min, // to będzie nowym minimum return( imin );
/* Przyklad 4.2 - SORTOWANIE TABLIC - metoda babelkowa */ #include <stdio.h> #define ROZMIAR 11 /* liczba elementów tablicy */ #define NIE 0 #define TAK 1 void sortuj( int dane[ ], int N ); /* funkcja sortująca */ /* program glowny ---------------------------------------------------------------- */ void main() int tab[ ROZMIAR ] = 0, 9, 2, 7, 4, 5, 6, 3, 8, 1, 10 ; int i; sortuj( tab, ROZMIAR ); /* uporzadkuj dane ------------ */ for( i=0; i < ROZMIAR; i++) /* wydrukuj wynik sprzatania */ printf(" %d \n", tab[ i ] ); /* Funkcja pomocnicza----------------------------------------------------------- */ void sortuj( int dane[ ], int N ) int x, i, j, powtorz = TAK; for( i=0; (i < N-1) && powtorz==tak; i++ ) // fale bąbli powtorz = NIE; for( j=0; j < N-i-1; j++) // jesli niepoprawna kolejność if (dane[ j ] > dane[ j+1 ]) // to bąbel, powtorz = TAK; // czyli zamiana miejscami x = dane[ j ]; dane[j] = dane[j+1]; dane[j+1] = x;
/* OPIS PROGRAMU 1) Algorytm składa się z tzw. fal bąbli. 2) W każdej fali bierze się po kolei dwa sąsiednie elementy, tzn. 0 i 1, 1 i 2, 2 i 3,... i zamienia się je miejscami ( bąbel!), jeśli element o mniejszym indeksie ma większą wartość. 3) Po każdej fali największy element zostaje przesunięty na koniec zbioru. Dlatego każda następna fala jest o jeden element krótsza. 4) Jeśli w jakiejś fali nie wstąpił żaden bąbel to znaczy, że wszystkie elementy są uporządkowane od najmniejszego do największego. 5) Dlatego kolejną falę przeprowadza się tylko wtedy kiedy w poprzedniej wystąpił bąbel oraz nie wszystkie fale zostały wykonane (dla zbioru N elementowego wykonuje się N-1 fal). 6) Przed dojściem do elementu największego (dryblasa) bąblowanie prowadzi do wstępnego porządkowania elementów mniejszych. Potem dryblas ze wszystkimi wygrywa i przesuwa się na koniec zbioru. 7) Jeśli zbiór jest uporządkowany, to zostaje wykonana tylko jedna fala bąbli. */
/* Przyklad 4.3a - SORTOWANIE TABLIC */ /* - metoda szybka - ang. QuickSort */ /* - wersja rekurencyjna */ #include <stdio.h> #define ROZMIAR 11 // liczba danych w tablicy void sortuj( int dane[ ], int dol, int gora ); // funkcja sortująca void wydruk( int dane[ ], int N ); // wydruk tablicy /* program glowny --------------------------------------------------------- */ void main() // int tab[ ROZMIAR ] = 0,9,2,7,4,5,6,3,8,1,10 ; /* ver.1 */ int tab[ ROZMIAR ] = 25,57,48,37,12,92,86,33 ; /* ver.2 */ sortuj( tab, 0, ROZMIAR-1 ); wydruk( tab, ROZMIAR ); /* Funkcja wydruku ----------------------------------------------------- */ void wydruk( int dane[ ], int N ) int i; for( i=0; i < N; i++) printf(" %2d ", dane[ i ] );
/* Funkcja sortujaca --------------------------------------------------------------- */ // Sortuj elementy tablicy dane[], zaczynając od tego o indeksie dol, // a kończąc na tym o indeksie gora void sortuj( int dane[ ], int dol, int gora ) int id, ig; // robocze indeksy id (dolny -->) i górny ig ( <-- ig) int a, x; // id idzie do góry, zaś ig - do dołu id = dol; ig = gora; a = dane[ dol ]; // inicjalizacja id, ig, a while ( id < ig ) // podzial // zbioru while ( (dane[ id ] <= a) && (id < gora) ) id++; // na while ( (a < dane[ ig ] ) && (dol < ig ) ) ig--; // elementy if ( id < ig ) // mniejsze // i x = dane[ id ]; // wieksze dane[ id ] = dane[ ig ]; // od dane[ ig ] = x; // elementu // pierwszego // dane[ dol ] = dane[ ig ]; // dane[ ig ] = a; // if ( dol < ig-1 ) sortuj( dane, dol, ig-1 ); // teraz sortuj poniżej if ( ig+1 < gora ) sortuj( dane, ig+1, gora ); // a potem - powyżej /* OPIS PROGRAMU 1) Indeks id idzie do góry, kiedy poniżej zostaje element mniejszy lub równy od elementu pierwszego a=dane[dol] oraz kiedy są jeszcze elementy powyżej. W pewnym momencie blokuje się na elemencie większym od pierwszego. 2) Indeks ig idzie do dołu, kiedy powyżej zostaje element większy od elementu pierwszego a=dane[dol] oraz kiedy są jeszcze elementy poniżej. W pewnym momencie blokuje na elemencie mniejszym od pierwszego. 3) W tym momencie następuje zamiana miejscami elementów blokujących : za duży jest przesunięty do góry, a za mały - na dól. 4) Po odblokowaniu, indeksy id i ig ponownie przesuwają się dopóki jest spełniony warunek id < ig. 5) Kiedy nie jest już on spełniony, indeks ig pokazuje na ostatni element mniejszy od pierwszego. Dlatego następuje wówczas zamiana miejscami elementów dane[dol] i dane[ig] 6) Następnie funkcja wykonuje się rekurencyjnie na elementach o indeksach mniejszych od ig [dol, ig-1] oraz na elementach większych od ig [ig+1, gora]. */
/* Przyklad 4.3b - SORTOWANIE TABLIC */ /* metoda szybka ang. QuickSort */ // Wersja nierekurencyjna z tablicy jako stosu (statycznie) // UWAGA: analiza programu dopiero po wykładzie nr 10 #include <stdio.h> #include <conio.h> #include <stdlib.h> /* definicja strukur danych --------------------------------------------------------- */ #define ROZMIAR 11 #define MAXSTOS 1000 // liczba elementów tablicy // wielkość stosu programowego #define TAK 1 #define NIE 0 struct typdg // definicja nowego typu typdg zmiennych // każda zmienna złożona tego typu int dol; // będzie się składała z dwóch liczb int gora; // np. struct typdg x; // deklaracja ; // x.dol = 0; x.gora = 10; // inicjalizacja struct typstos // definicja nowego typu typstos zmiennych // np. struct typstos x; int top; // x.top = -1; struct typdg granice[ MAXSTOS ]; // x.granice[0].dol = 0; ; // x.granice[0].gora = 0; /* deklaracja sposobu wywołania funkcji -------------------------------------- */ int pustystos( struct typstos *ps ); void nastos( struct typstos *ps, struct typdg *pdg ); void zestosu( struct typstos *ps, struct typdg *pdg ); void sortuj( int dane[ ], int N ); void podzial( int dane[ ], int dol, int gora, int *srodek ); void wydruk( int dane[ ], int N );
/* program glowny ------------------------------------------------------ */ void main() // int tab[ ROZMIAR ] = 0,9,2,7,4,5,6,3,8,1,10 ; /* ver.1 */ int tab[ ROZMIAR ] = 25,57,48,37,12,92,86,33 ; /* ver.2 */ int i; sortuj( tab, ROZMIAR ); wydruk( tab, ROZMIAR ); /* Funkcje pomocnicza------------------------------------------------- */ void sortuj( int dane[ ], int N ) int i, j; struct typdg nowedg; // deklaracja zmiennej nowedg typu typdg struct typstos stos; // deklaracja zmiennej stos typu typstos stos.top = -1; nowedg.dol = 0; nowedg.gora = N-1; nastos( &stos, &nowedg ); printf(" " ); for( i=0; i < N; i++) printf(" %2d ", i ); printf("\n"); // nic nie ma na stosie // indeks dolny tablicy do sortowania // indeks górny tablicy do sortowania // zapisz na stos // pomocniczy wydruk
while(!pustystos( &stos ) ) /* powtarzaj, w 1-ej kolejności */ /* podtablice MNIEJSZE */ zestosu( &stos, &nowedg ); /* pobierz ze stosu dół i górę */ while ( nowedg.gora > nowedg.dol ) /* 1-sze tab MNIEJSZE */ printf(" d=%2d g=%2d ", nowedg.dol, nowedg.gora ); wydruk( dane, N ); /* aktualna kolejnosc */ podzial( dane, nowedg.dol, nowedg.gora, &j); printf(" s=%2d\n", j ); /* zwrocony srodek */ if ( j-nowedg.dol > nowedg.gora-j ) /* zapisz na stos dolna WIEKSZA podtablice */ i = nowedg.gora; nowedg.gora = j-1; nastos( &stos, &nowedg ); /* przetwarzaj gorna MNIEJSZA podtablice */ nowedg.dol = j+1; nowedg.gora = i; else /* zapisz na stos gorna WIEKSZA podtablice */ i = nowedg.dol; nowedg.dol = j+1; nastos( &stos, &nowedg ); /* przetwarzaj dolna MIEJSZA podtablice */ nowedg.dol = i; nowedg.gora = j-1; /* end if */ /* end while gora > dol */ /* end while pustystos */ printf(" " ); /* end sortuj */
/* Podzial zbioru ------------------------------------------------------------------ */ /* funkcja przestawia elementy, zwraca indeks srodka = mediany */ /* identyczna jak w programie c4_3a.c */ void podzial( int dane[ ], int dol, int gora, int *srodek ) int a, id, ig, x; a = dane[ dol ]; /* szukamy nowej pozycji tego elementu */ id = dol; /* indeks roboczy dolny */ ig = gora; /* indeks roboczy gorny */ while( id < ig ) while( (dane[ id ] <= a) && (id < gora) ) id++; /* idz do gory */ while( (dane[ ig ] > a) && (ig > dol) ) ig--; /* idz do dolu */ if ( id < ig ) x = dane[ id ]; /* zamien miejscami */ dane[ id ] = dane[ ig ]; /* elementy */ dane[ ig ] = x; /* jesli id < ig */ dane[ dol ] = dane[ ig ]; /* koncowa zamiana */ dane[ ig ] = a; /* dol <--> ig */ *srodek = ig; /* zwroc indeks srodka */
/* OPERACJE NA STOSIE -------------------------------------------- */ int pustystos( struct typstos *ps ) if ( ps->top == -1 ) // zapis: *ps.top = ps->top return( TAK ); else return( NIE ); /* ------------------------------------------------------------------------------ */ void zestosu( struct typstos *ps, struct typdg *pdg ) if ( pustystos(ps) ) printf("stos jest pusty!\n"); exit(1); pdg->dol = ps->granice[ ps->top ].dol; // *ps.granice[ *ps.top ].dol pdg->gora = ps->granice[ ps->top ].gora; // *ps.granice[ *ps.top ].gora (ps->top)--; /* ------------------------------------------------------------------------------ */ void nastos( struct typstos *ps, struct typdg *pdg ) if (ps->top == MAXSTOS-1) printf("blad! Stos jest przepelniony!\n"); exit(1); else (ps->top)++; // (*ps.top)++ ps->granice[ ps->top ].dol = pdg->dol; // jak wyżej ps->granice[ ps->top ].gora = pdg->gora; // jak wyżej
/* OPIS PROGRAMU Jak widać niewykorzystanie funkcji rekurencyjnej bardzo komplikuje algorytm sortowania metodą QuickSort. Po umieszczeniu pierwszego elementu tablicy na poprawnej pozycji, musimy powtórzyć tę samą operację podziału dla dwóch zbiorów elementów: leżących poniżej oraz powyżej niego. Indeks początkowy i końcowy większego zbioru zapisujemy na stosie, a przetwarzamy dalej mniejszy zbiór elementów tablicy. Kiedy warunek: while ( nowedg.gora > nowedg.dol ) nie jest spełniony, zaczynamy zdejmować ze stosu to co zostało tam zapisane, czyli dolne i górne indeksy podtablic, które muszą być jeszcze posortowane: zestosu( &stos, &nowedg ); Funkcja kończy swoje działanie kiedy na stosie już nic nie ma: while(!pustystos( &stos ) ) go Johny, go!. Na początku kładziemy na stosie dolny i górny indeks całej tablicy. stos.top = -1; // nic nie ma na stosie nowedg.dol = 0; // indeks dolny tablicy do sortowania nowedg.gora = N-1; // indeks górny tablicy do sortowania nastos( &stos, &nowedg ); // zapisz na stos Budowę stosu pokazuje poniższy rysunek. Parametr top wskazuje na ostatnią dwuliczbę typu dól/góra zapisaną w tablicy granice[]. top d0 d1 d2 d3 g0 g1 g2 g3 granice[ MAXSTOS ] */ dol gora