ALGORYTMY SORTOWANIA DANYCH W zagadnieniu sortowania danych rozpatrywa b dziemy n liczb caªkowitych, b d cych pierwotnie w losowej kolejno±ci, które nale»y uporz dkowa nierosn co. Oczywi±cie sortowa mo»emy te» inne typy danych ni» liczby caªkowite (np. liczby zmiennoprzecinkowe, ci gi znaków alfanumerycznych, itp.), jednak idea algorytmów nie zmienia si. Podobnie, je±li chodzi o sortowanie w porz dku odwrotnym do zaªo»onego (niemalej co) zmiany sprowadzaj si praktycznie do zamiany znaków mniejszo±ci / wi kszo±ci na przeciwne.
Najprostszym jest algorytm sortowania b belkowego, który mo»na zaimplementowa jako dwie zagnie»d»one p tletypu for, z których ka»da wykonuje si O(n) razy. Zadaniem tych p tli jest umieszczenie ka»dego z elementów na wªa±ciwej pozycji. int B[n]; void bubble(int* B) { for(j=1; j<n; j++) for(i=n-1; i>0; i--) if(b[i-1]>b[i]) { temp=b[i]; B[i]=B[i-1]; B[i-1]=temp; }; }; O(n) O(n) = O(n 2 ).
Zªo»ono± obliczeniowa sortowania b belkowego jest zawsze O(n 2 ) ka»da z p tli wykona si zawsze O(n) razy, chocia» np. w sytuacji wst pnego posortowania danych wej±ciowych liczb obiegów p tli wewn trznej mo»na by zmniejszy. Uwzgl dnia to algorytm sortowania przez wstawianie, gdzie wewn trzna p tla szuka wªa±ciwej pozycji dla bie» cego elementu, ale tylko w±ród elementów wcze±niej posortowanych:
int B[n]; void insert(int* B) { for(i=2;i<n+1;i++) { j=i-1; while(b[j]<b[j-1]) { pom=b[j]; B[j]=B[j-1]; B[j-1]=pom; j--; if(j<1) break; }; }; }; O(i) O(n) Czy takie usprawnienie przekªada si na lepsz zªo»ono± obliczeniow?
Liczba iteracji p tli wewn trznej za pierwszym razem (tj. przy pierwszej iteracji p tli zewn trznej) wynosi maksymalnie 1, za drugim razem 2, potem 3, itd. Najwi cej razy wykona si w ostatniej ((n 1)-szej) iteracji p tli zewn trznej n 1 razy. Zatem, aby wyznaczy zªo»ono± obliczeniow caªej procedury sortowania przez wstawianie, wystarczy policzy sum nast puj cego ci gu: 1 + 2 + 3 +... + (n 2) + (n 1) = n 1 j=1 n 1 j=1 j = 1 2 (n 1)n = 1 2 n2 1 2 n = O(n2 ). j, Uzyskali±my wi c algorytm tego samego rz du co algorytm sortowania b - belkowego.
Jednak rozwa»my przypadek, gdy liczby s ju» posortowane. (Przypomnijmy,»e w takiej sytuacji algorytm sortowania b belkowego wykona tyle samo operacji, co w przypadku danych nieposortowanych). W takim przypadku, w algorytmie sortowania przez wstawianie p tla wewn trzna nie wykona si ani razu w ka»dej z n 1 iteracji p tli zewn trznej. Zatem czas dziaªania algorytmu ograniczy si do O(n).
Sortowanie przez kopcowanie Wykorzystajmy teraz struktur kopca do skonstruowania algorytmu sortowania. Idea sprowadza si do nast puj cych kroków: 1. Umie± wszystkie dane wej±ciowe w kopcu. Utwórz pust tablic wyj±ciow o dªugo±ci n. 2. Pobierz element z korzenia na pierwsz woln pozycj w tablicy wyj- ±ciowej. 3. Umie± w korzeniu (w miejsce pobranego elementu) ostatni element z kopca (rozmiar kopca zmniejsza si o 1). Przywró wªa±ciwo± kopca (analogicznie, jak w procedurze usuwania elementu z kopca). 4. Je±li kopiec nie jest pusty to skocz do Kroku 2; w przeciwnym wypadku STOP posortowane dane s w tablicy wyj±ciowej.
Wyznaczmy teraz zªo»ono± obliczeniow algorytmu sortowania przez kopcowanie. Po pierwsze, nale»y zauwa»y,»e w algorytmie kroki 2-4 s powtarzane n razy (w ka»dej iteracji, kopiec pocz tkowo n elementowy zmniejsza si o 1). Zatem zªo»ono± caªej procedury mo»na wyliczy jako: Zªo»ono± Kroku 1 + n Zªo»ono± Kroków 2-4. Wyznaczmy wi c zªo»ono± poszczególnych kroków algorytmu.
Zªo»ono± Kroku 1: Utworzenie kopca z n losowych danych to, jak ju» wspomniano, n-krotne wykonanie operacji wstawienia elementu do kopca. Wychodz c z zaªo»enia,»e wstawienie elementu do kopca n elementowego zajmuje O(log n) czasu (tyle, ile wynosi wysoko± kopca), zªo»ono± Kroku 1 mo»na oszacowa jako O(n log n). Oczywi±cie 2-ga cz ± Kroku 1 utworzenie tabl. wyj±c. zajmuje O(1). UWAGA: Utworzenie kopca mo»na te» wykona szybciej w sposób wst puj cy. W metodzie tej zakªadamy,»e od pocz tku kopiec jest wypeªniony losowymi danymi. Rozpoczynaj c od elementu n/2 przechodzimy przez kolejne elementy a» do pierwszego i za ka»dym razem wykonujemy operacj naprawy cz ±ci kopca le» cego poni»ej (tj. poddrzewa, dla którego bie-» cy element jest korzeniem), na podobnej zasadzie jak naprawa kopca po usuni ciu elementu.
W takiej sytuacji mo»na wykaza,»e w kopcu jest najwy»ej n/2 k+1 elementów maj cych poni»ej siebie k poziomów. Dla ka»dego takiego elementu operacja naprawy cz ±ci kopca le» cego poni»ej zajmujeo(k) czasu. Zatem zªo»ono± Kroku 1 mo»na wyliczy jako: log n k=0 n 2 k+1 O(k) = O n log n k=0 k 2 k. Korzystaj c teraz z zale»no±ci (patrz np. Cormen i in.) otrzymamy O n k=0 log n k=0 k 2 k = 1/2 (1 1/2) 2 = 2, k 2 k = O n k=0 k 2 k = O(n).
Zªo»ono± Kroków 2 i 4 to oczywi±cie O(1), natomiast Kroku 3 O(log n). Zatem zªo»ono± caªego algorytmu to O(n) + n (O(1) + O(log n) + O(1)) = O(n log n).
8 3 1 [3] [4] [5] [6] [7] 5 6 9 2 [8] [9] [10] 12 7 4
8 3 1 [3] [4] [5] [6] [7] 5 6 9 2 [8] [9] [10] 12 7 4
8 3 1 [3] [4] [5] [6] [7] 5 6 9 2 [8] [9] [10] 12 7 4
8 3 1 [3] [4] [5] [6] [7] 5 6 9 2 [8] [9] [10] 12 7 4
8 3 1 [3] [4] [5] [6] [7] 12 6 9 2 [8] [9] [10] 5 7 4
8 3 1 [3] [4] [5] [6] [7] 12 6 9 2 [8] [9] [10] 5 7 4
8 3 9 [3] [4] [5] [6] [7] 12 6 1 2 [8] [9] [10] 5 7 4
8 3 9 [3] [4] [5] [6] [7] 12 6 1 2 [8] [9] [10] 5 7 4
8 12 9 [3] [4] [5] [6] [7] 7 6 1 2 [8] [9] [10] 5 3 4
8 12 9 [3] [4] [5] [6] [7] 7 6 1 2 [8] [9] [10] 5 3 4
12 8 9 [3] [4] [5] [6] [7] 7 6 1 2 [8] [9] [10] 5 3 4
12 8 9 [3] [4] [5] [6] [7] 7 6 1 2 [8] [9] [10] 5 3 4 Tablica wyjściowa:
8 9 [3] [4] [5] [6] [7] 7 6 1 2 [8] [9] [10] 5 3 4 Tablica wyjściowa: 12
8 9 [3] [4] [5] [6] [7] 7 6 1 2 [8] [9] [10] 5 3 4 Tablica wyjściowa: 12
4 8 9 [3] [4] [5] [6] [7] 7 6 1 2 [8] [9] 5 3 Tablica wyjściowa: 12
9 8 4 [3] [4] [5] [6] [7] 7 6 1 2 [8] [9] 5 3 Tablica wyjściowa: 12
8 4 [3] [4] [5] [6] [7] 7 6 1 2 [8] [9] 5 3 Tablica wyjściowa: 12 9
8 4 [3] [4] [5] [6] [7] 7 6 1 2 [8] [9] 5 3 Tablica wyjściowa: 12 9
3 8 4 [3] [4] [5] [6] [7] 7 6 1 2 [8] 5 Tablica wyjściowa: 12 9
8 7 4 [3] [4] [5] [6] [7] 5 6 1 2 [8] 3 Tablica wyjściowa: 12 9
7 4 [3] [4] [5] [6] [7] 5 6 1 2 [8] 3 Tablica wyjściowa: 12 9 8
7 4 [3] [4] [5] [6] [7] 5 6 1 2 [8] 3 Tablica wyjściowa: 12 9 8
3 7 4 [3] [4] [5] [6] [7] 5 6 1 2 Tablica wyjściowa: 12 9 8
7 6 4 [3] [4] [5] [6] [7] 5 3 1 2 Tablica wyjściowa: 12 9 8
6 4 [3] [4] [5] [6] [7] 5 3 1 2 Tablica wyjściowa: 12 9 8 7
6 4 [3] [4] 5 3 [5] [6] [7] 1 2 Tablica wyjściowa: 12 9 8 7
2 6 4 [3] [4] 5 3 [5] [6] 1 Tablica wyjściowa: 12 9 8 7
6 5 4 [3] [4] 2 3 [5] [6] 1 Tablica wyjściowa: 12 9 8 7
5 4 [3] [4] 2 3 [5] [6] 1 Tablica wyjściowa: 12 9 8 7 6
5 4 [3] [4] 2 3 [5] [6] 1 Tablica wyjściowa: 12 9 8 7 6
1 5 4 [3] [4] 2 3 [5] Tablica wyjściowa: 12 9 8 7 6
5 3 4 [3] [4] 2 1 [5] Tablica wyjściowa: 12 9 8 7 6
3 4 [3] [4] 2 1 [5] Tablica wyjściowa: 12 9 8 7 6 5
3 4 [3] [4] 2 1 [5] Tablica wyjściowa: 12 9 8 7 6 5
1 3 4 [3] [4] 2 Tablica wyjściowa: 12 9 8 7 6 5
4 3 1 [3] [4] 2 Tablica wyjściowa: 12 9 8 7 6 5
3 1 [3] [4] 2 Tablica wyjściowa: 12 9 8 7 6 5 4
3 1 [3] [4] 2 Tablica wyjściowa: 12 9 8 7 6 5 4
2 3 1 [3] Tablica wyjściowa: 12 9 8 7 6 5 4
3 2 1 [3] Tablica wyjściowa: 12 9 8 7 6 5 4
2 1 [3] Tablica wyjściowa: 12 9 8 7 6 5 4 3
2 1 [3] Tablica wyjściowa: 12 9 8 7 6 5 4 3
1 2 Tablica wyjściowa: 12 9 8 7 6 5 4 3
2 1 Tablica wyjściowa: 12 9 8 7 6 5 4 3
1 Tablica wyjściowa: 12 9 8 7 6 5 4 3 2
1 Tablica wyjściowa: 12 9 8 7 6 5 4 3 2
1 Tablica wyjściowa: 12 9 8 7 6 5 4 3 2
Tablica wyjściowa: 12 9 8 7 6 5 4 3 2 1
KONIEC Tablica wyjściowa: 12 9 8 7 6 5 4 3 2 1
Algorytm Quicksort Zasad dziaªania tego algorytmu najlepiej przedstawi przy pomocy nast puj cej procedury rekurencyjnej: int B[n]; void Quicksort(int* B,int p,int r) { if(p<r) { q=partition(b,p,r); }; Quicksort(B,p,q); Quicksort(B,q+1,r); }; Kluczowym elementem algorytmu jest procedura Partition. Procedura ta ma za zadanie taki podziaª obszaru tablicy B od indeksu p do indeksu q, aby przed elementem q znalazªy si elementy nie wi ksze od q-tego, a po tym elemencie nie mniejsze.
Aby posortowa caª tablic B, nale»y wywoªa Quicksort(B,1,n). Zatem caªa tablica dzielona jest na 2 cz ±ci: w pierwszej z nich znajd si elementy nie wi ksze ni» w drugiej. Nast pnie ka»da z tych cz ±ci dzielona jest na dwie mniejsze o takiej samej wªasno±ci, itd. Algorytm ko«czy dziaªanie, gdy osi gnie wyª cznie obszary 1- lub 2- elementowe, i w ka»dym z 2-elementowych mniejszy element zostanie ustawiony przed wi kszym (oczywi±cie elementy równe mog pozosta w dowolnej kolejno±ci).
Jedn z decyzji, jak nale»y podj przy konstruowaniu procedurypartition jest wybór elementu q, wzgl dem którego b dzie dokonany podziaª elementów B[p]... B[r]. Jednym z najprostszych sposobów jest wybór elementu skrajnego, np. B[r]. Nast pnie analizowane s kolejno wszystkie pozostaªe elementy i umieszczane w pierwszym albo w drugim obszarze(w zale»no±ci od tego, czy dany element jest odpowiednio mniejszy czy wi kszy od q-tego). Najlepiej, je±li powy»sz operacj wykonamy w miejscu. Zilustrujmy powy»sze rozwi zanie na przykªadzie.
B: Partition (B, p, r ) [p] [r] 2 8 7 1 3 5 6 4
B: Partition (B, p, r ) [p] [r] 2 8 7 1 3 5 6 4 q
B: Partition (B, p, r ) [p] [r] 2 8 7 1 3 5 6 4
B: Partition (B, p, r ) [p] [r] 2 8 7 1 3 5 6 4
B: Partition (B, p, r ) [p] [r] 2 8 7 1 3 5 6 4
B: Partition (B, p, r ) [p] [r] 2 8 7 1 3 5 6 4
B: Partition (B, p, r ) [p] [r] 2 8 7 1 3 5 6 4
B: Partition (B, p, r ) [p] [r] 2 1 7 8 3 5 6 4
B: Partition (B, p, r ) [p] [r] 2 1 7 8 3 5 6 4
B: Partition (B, p, r ) [p] [r] 2 1 7 8 3 5 6 4
B: Partition (B, p, r ) [p] [r] 2 1 3 8 7 5 6 4
B: Partition (B, p, r ) [p] [r] 2 1 3 8 7 5 6 4
B: Partition (B, p, r ) [p] [r] 2 1 3 8 7 5 6 4
B: Partition (B, p, r ) [p] [r] 2 1 3 8 7 5 6 4
B: Partition (B, p, r ) [p] [r] 2 1 3 4 7 5 6 8
Zªo»ono± obliczeniowa procedury Partition wynosi O(n). A jaka jest zªo»ono± caªego algorytmu? Zale»y to ±ci±le od dokonywanych podziaªów. Je±li za ka»dym razem wybierany jest do podziaªu taki element, który dzieli dany obszar na 2 równe cz ±ci, to uzyskamy zªo»ono± O(n log n), co uzasadnia poni»szy rysunek.
n n n / 2 n / 2 n log n n / 4 n / 4 n / 4 n / 4 n n / 8 n / 8 n / 8 n / 8 n / 8 n / 8 n / 8 n / 8 n 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 n czas pojedynczego wywołania Partition O(n log n)
Jednak je±li do podziaªu wybierany jest zawsze taki element,»e jedna cz ± dzielonego obszaru zawiera 1 element, to uzyskamy wynik O(n 2 ). n n 1 n 1 n n 1 n 2 n 1 1 n 3 n 2 2 3 1 1 2 O(n 2 )
Na koniec nale»y zauwa»y,»e wystarczy, aby podziaª byª dokonywany w proporcji 9/1, aby zªo»ono± algorytmu Quicksort wynosiªa O(n log n). Czy mo»na skonstruowa algorytm sortowania o zªo»ono±ci mniejszej ni» O(n log n)?
Wszystkie dotychczas przedstawione algorytmy s algorytmami dziaªaj cymi na zasadzie porówna«. Mo»na wykaza,»e algorytmy tego typu nie mog mie zªo»ono±ci mniejszej ni» O(n log n). Zatem konstruuj c algorytm sortowania przez kopcowanie osi gn li±my doln granic. Jednak przy pewnych dodatkowych zaªo»eniach (np. odno±nie warto±ci danych wej±ciowych) mo»na uzyska lepszy rezultat.
Sortowanie przez zliczanie W algorytmie tym mamy dodatkow tablic, C, o rozmiarze równym maksymalnej warto±ci spo±ród danych wej±ciowych, powiedzmy m. Wszystkie komórki tej tablicy s zainicjowane warto±ci 0. Dziaªanie algorytmu sprowadza si do sprawdzenia ka»dej warto±ci tablicy wej±ciowej i zwi kszenie o 1 tej komórki tablicy C, która odpowiada warto±ci analizowanej danej (np., je±li w danej komórce tablicy wej±ciowej odczytamy 5, to inkrementujemy komórk C[5]). Po przeanalizowaniu wszystkich n danych wej±ciowych, w tablicy C mamy informacj, ile w±ród tych danych byªo warto±ci 1, ile warto±ci 2, itd. Tablic wyj±ciow wypeªniamy wi c tak,»e do pierwszych C[m] komórek wpisujemy m, do nast pnych C[m 1] komórek m 1, itd., a do ostatnich C komórek wpisujemy warto± 1.
Zªo»ono± obliczeniowa algorytmu wynosi O(n + m): ka»d dan wej±ciow analizujemy 1 raz, za ka»dym razem inkrementacja wybranej komórki tablicy C zajmuje O(1), na koniec wypeªnienie tablicy wyj±ciowej wymaga przej±cia po wszystkich elementach tablicy C. Dla m = O(n) algorytm ma wi c zªo»ono± O(n).
Sortowanie pozycyjne Sortowanie n liczb d-cyfrowych polega na wykonaniu d sortowa«najpierw wg najmniej znacz cej cyfry, pó¹niej bardziej znacz cej, itd. Na ko«cu sortujemy wg najbardziej znacz cej cyfry. Trzeba tylko uwzgl dnia kolejno± z poprzedniego sortowania, je±li cyfry na bie» cej pozycji s jednakowe. Poniewa» cyfry w systemie np. dziesi tnym maj maksymalnie warto± 9, mo»emy z powodzeniem wykorzysta do sortowania wg poszczególnych pozycji algorytm sortowania przez zliczanie. Zªo»ono± obliczeniowa wynosi wtedy O(dn + dm), gdzie m jest maksymaln warto±ci cyfr (np. m = 9 dla systemu dziesi tnego). Je±li d jest staª, a m = O(n), to otrzymujemy zªo»ono± O(n).
Sortowanie kubeªkowe Przy zaªo»eniu,»e dane wej±ciowe s z okre±lonego zakresu, np. [0,1), dzielimy ten przedziaª na n obszarów o jednakowym rozmiarze (tworzymy n odpowiadaj cych im kubeªków). Ka»da dana wpada wi c do odpowiedniego kubeªka (je±li dane wej±ciowe s z rozkªadu jednostajnego, w ka»dym kubeªku znajdzie si mniej wi cej tyle samo elementów). Nast pnie dane wewn trz ka»dego kubeªka sortujemy, np. algorytmem przez wstawianie, i zª czamy kubeªki razem, otrzymuj c poprawny ci g wynikowy. Mo»na wykaza,»e zªo»ono± obliczeniowa wynosi O(n).