Przedstawiamy algorytmy porządkowania dowolnej liczby elementów, którymi mogą być liczby, jak również elementy o bardziej złożonej postaci (takie jak słowa i daty). Porządkowanie, nazywane również często sortowaniem (Steinhaus używa terminu szeregowanie), ma olbrzymie znaczenie niemal w każdej działalności człowieka. Jeśli elementy w zbiorze są uporządkowane zgodnie z jakąś regułą (np. książki lub ich karty katalogowe według liter alfabetu, słowa w encyklopedii, daty, czy nawet osoby według wzrostu), to wykonywanie wielu operacji na tym zbiorze staje się łatwiejsze. Między innymi dotyczy to operacji: sprawdzenia, czy dany element, tj. element o ustalonej wartości cechy, według której zbiór został uporządkowany, znajduje się w zbiorze; znalezienia elementu w zbiorze, gdy w nim jest; dołączenia nowego elementu w odpowiednie miejsce, aby zbiór pozostał nadal uporządkowany. 1
Komputery w dużym stopniu zawdzięczają swoją szybkość temu, że działają na uporządkowanych informacjach. Jeśli chcemy na przykład sprawdzić, czy w jakimś katalogu dyskowym znajduje się plik, to najpierw odpowiednio porządkujemy listę plików. Producenci komputerów w 1973 r. szacowali, że przez ponad 25% czasu pracy komputera jest wykonywane porządkowanie. Obecnie porządkowanie to (wobec większego skomplikowania programów) zajmuje jeszcze więcej czasu. (Porządkowanie jest także podstawową operacją wykonywaną na bazach danych.) Omawiane algorytmy porządkowania mogą być stosowane do ciągów zawierających dowolna liczbę elementów. Dwa pierwsze algorytmy są realizacjami bardzo naturalnych pomysłów wynikających wprost z definicji problemu porządkowania. Omówimy także sposób sortowania informacji innych niż liczby (słowa lub rekordy). 2
Algorytm bąbelkowy Opiera się on na bardzo prostej koncepcji: jeśli ciąg nie jest uporządkowany, to znajdują się w nim przynajmniej dwa elementy, które nie są na właściwych miejscach - przestawiamy je i postępujemy tak, aż wszystkie będą na właściwych miejscach. Można nieco uprościć to postępowanie. Zauważmy, że: jeśli ciąg zawiera dwa elementy, które nie są jeszcze ustawione we właściwej kolejności, to istnieją w nim dwa elementy, które stoją obok siebie i nie są we właściwej kolejności. Na przykład, w ciągu {2, 4, 6, 1, 3, 5} elementy 4 i 3 nie są we właściwym miejscu, ale istnieją dwa elementy 6 i 1 stojące obok siebie, które również nie są odpowiednio uszeregowane. Z kolei po ich przestawieniu, elementy 4 i 1 oraz 6 i 3 znajdą się na niewłaściwych pozycjach. Zatem, w ciągu nieuporządkowanym wystarczy przestawić ze sobą elementy, które są ustawione w złym porządku i znajdują się na sąsiednich miejscach. 3
Aby tę strategię porządkowania zamieniæ na algorytm, musimy określić dodatkowo, w jakim porządku będziemy szukać takich par do przestawienia. Z jednej strony bowiem nie możemy pominąć żadnej z nich. Z drugiej - chcemy to robić w możliwie najbardziej efektywny sposób. Algorytm bąbelkowy jest metodą porządkowania ciągów, która polega na przestawianiu sąsiednich par elementów stojących w nieodpowiedniej kolejności, przy czym ciąg jest przeglądany w tym samym kierunku tak długo, jak długo może w nim wystąpić jeszcze para elementów w niewłaściwym porządku. Oznacza to, że po każdym przestawieniu dwóch elementów należy sprawdzić, czy ta zmiana nie zepsuła właściwego uporządkowania między innymi sąsiednimi elementami. Elementy o większych wartościach są wynoszone wyżej, jakby przywiązane zostały do nich większe bąbelki powietrza - stąd nazwa algorytmu. Klamrami oznaczone są zamiany elementów. Poziome linie to granica o obszaru, w którym nie trzeba wykonywać już przestawień. Przykład ilustruje, że wraz z liczbą kroków zwiększa się liczba elementów, które stoją na właściwym miejscu. 4
Przykład sortowania bąbelkowego 9 10 10 10 10 7 9 9 9 9 1 7 7 7 7 10 1 4 4 4 4 4 1 3 3 2 3 3 1 2 3 2 2 2 1 5
Algorytm bąbelkowy porządkowania ciagu liczb Dane: Liczba naturalna n i ciąg n liczb x 1, x 2,...,x n Wynik: Uporządkowanie tego ci¹gu liczb od najmniejszej do największej. Krok 1: Kres := n {Kres określa miejsce w ciągu, oznaczone na rysunku czerwoną linią stanowiące granicę poszukiwania pary elementów do przestawiania.} Krok 2: Przyjmij i:=1 oraz k:= 0 {k jest wskaźnikiem przestawionej pary} Krok 3: Dopóki i < Kres, wykonuj: jeśli x i > x i + 1, to przestaw te dwa elementy i przyjmij k := i zwiększ i: i:= i + 1 Krok 4: Jeśli k > 1, to przyjmij Kres :=k i wróć do kroku 2, a w przeciwnym razie zakończ algorytm. 6
Porządkowanie przez wybór Algorytm opisany w tym punkcie można, podobnie jak algorytm bąbelkowy, wyprowadzić niemal z definicji problemu porządkowania. Zauważymy, że: jeśli mamy ustawić elementy ciągu w kolejności od najmniejszego do największego, to można wybrać w nim najpierw element najmniejszy i umieścić go na początku, za nim umieścić drugi najmniejszy element ciągu ( czyli najmniejszy w pozostałym ciągu) itd. Metoda ta nazywa się algorytmem porządkowania przez wybór. Do jej dokładnego opisania jesteśmy już przygotowani, gdyż poznaliśmy szybką (wręcz optymalną) metodę znajdowania najmniejszego elementu w ciągu. Musimy jedynie podać, w jaki sposób kolejno znajdowane elementy, od najmniejszego do największego, mają być ustawione jeden za drugim. Najoszczędniej byłoby robić to w tym samym miejscu, w którym jest zapisany ciąg do uporządkowania. Tego typu algorytm określany jest terminem in situ. 7
Jest to możliwe! Znaleziony element zmienia się miejscami z elementem, który zajmuje jego miejsce w uporządkowanym ciągu. Po znalezieniu najmniejszego elementu trzeba zamienić go miejscami z pierwszym elementem ciągu. W następnym etapie - drugi najmniejszy element w ciągu zamienić miejscami z drugim elementem ciągu itd. W takiej realizacji tej metody, wystarczy znajdować miejsce (a dokładniej - jego indeks) w ciągu zajmowane przez coraz większe elementy. UWAGA: Bieżący najmniejszy element może się już znajdować na swoim miejscu w ciągu. W prezentowanej realizacji algorytmu nie wyróżniamy tej sytuacji i zamieniamy element sam ze sobą. 8
Algorytm porządkowania przez wybór Dane: Liczba naturalna n i ciąg n liczb x 1, x 2,..., x n Wynik: Uporządkowanie tego ciągu liczb od najmniejszej do największej. Krok 1: Dla i = 1, 2,...,n -1 wykonaj kroki 2 i 3. Krok 2: Znajdź k takie, że x k jest najmniejszym elementem w podciągu x i,..., x n. Krok 3: Zamień miejscami elementy x i oraz x k. 9
Przykład porządkowania przez wybór 9 9 9 9 9 9 10 7 7 7 7 7 10 9 1 3 3 4 10 7 7 10 10 10 10 4 4 4 4 4 4 3 3 3 3 2 2 2 2 2 2 2 3 1 1 1 1 1 1 10
Złożoność obliczeniowa algorytmu. Aby znaleźć postać wyrażenia określającego liczbę działań (porównań i przestawień elementów) wykonywanych w algorytmie porządkowania przez wybór, wystarczy zauważyć, że jest on iteracją algorytmu znajdowania najmniejszego elementu, a ciąg, w którym szukamy najmniejszego elementu jest w kolejnych iteracjach coraz krótszy. Liczba przestawień elementów jest równa liczbie iteracji, a więc wynosi n-1. Liczba porównań, jest w algorytmie znajdowania minimum w ciągu jest o jeden mniejsza od liczby elementów w ciągu. Ponieważ w każdym kroku liczba elementów w poszukiwanym podciągu jest o jeden mniejsza, zatem w algorytmie porządkowania przez wybór, dla ciągu danych złożonego z n elementów, liczba porównań wynosi: (n -1) + (n - 1) +... + 2 + 1 = n (n + 1) / 2 11
12