Problem porządkowania zwanego również sortowaniem jest jednym z najważniejszych i najpopularniejszych zagadnień informatycznych. 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. Ponieważ algorytmy porządkowania służą do znajdywania uporządkowania liczb względem ich wartości wykonujemy: operacje porównania, a jeśli jest to konieczne ze względu na złe uporządkowanie porównywanych liczb zmieniamy ich kolejność, czyli przestawiamy je. Porównanie w algorytmach porządkowania oznacza zbadanie prawdziwości jednej z nierówności: słabej (<= lub >=) silnej (< lub >). W przypadku porządkowania dwóch liczb trudno jest sobie wyobrazić algorytm wykonujący więcej niż jedno porównanie. 1
Mając do uporządkowania trzy liczby możemy postąpić następująco: najpierw ustawiamy w odpowiedniej kolejności a i b, potem wstawiamy c w odpowiednie miejsce względem a i b. W pierwszym etapie wykonujemy dokładnie jedno porównanie. W drugim etapie możemy (jeśli a<b) mieć następujące sytuacje: jeśli c<a to (c, a, b) jest szukanym uporządkowaniem, w przeciwnym wypadku należy sprawdzić, czy c<b. Jeśli tak, to mamy uporządkowanie (a, c, b) - w przeciwnym wypadku będzie (a, b, c). a<b 1 0 c<a c<b 1 0 1 0 (c, a, b) b<c (c, b, a) c<a 1 0 1 0 (a, b, c) (a, c, b) (b, c, a) (b, a, c) 2
Liczba porównań prowadzących do wyróżnionej odpowiedzi algorytmu jest równa liczbie wierzchołków pośrednich na drodze z korzenia do wierzchołka końcowego - tę liczbę będziemy nazywali długością takiej drogi. Dwa wyniki otrzymujemy po wykonaniu dwóch porównań. Cztery pozostałe po trzech. Największa długość drogi z korzenia do wierzchołka końcowego, nazywana wysokością drzewa algorytmu, jest największą liczbą operacji wykonywanych w algorytmie dla jakiegokolwiek możliwego układu danych. Wielkość ta nazywa się złożonością obliczeniową lub pracochłonnością algorytmu. Jest to najczęściej złożoność pesymistyczna (złożoność najgorszego przypadku danych) - w niektórych przypadkach algorytm może działać szybciej. W najmniej korzystnym (zwanym także najgorszym) przypadku danych: algorytm reprezentowany w postaci drzewa algorytmu jest tym lepszy (szybszy) im niższe jest to drzewo (im mniejszą ma wysokość). Algorytmy porządkowania kilku liczb wykonują swoje zadanie za pomocą najmniejszej możliwej liczby porównań. Są to więc algorytmy optymalne pod względem pracochłonności. 3
Uporządkowanie czterech liczb a, b, c i d można znaleźć w dwóch krokach: uporządkować najpierw trzy liczby a, b i c a następnie umieścić czwartą w ciągu trzech liczb. Ten sposób wstawiania czwartej liczby do trójki uporządkowanych liczb jest szczególnym przypadkiem algorytmu binarnego umieszczania. Liczbę d należy najpierw porównać ze środkową liczbą z trzech uporządkowanych!!! Wtedy wysokość drzewa wzrośnie o 2. Jeśli porównanie rozpoczniemy od pierwszej albo ostatniej liczby z trzech wysokość drzewa wzrośnie o 3. Porządkowanie pięciu liczb można przeprowadzić rozszerzając algorytm porządkowania czterech liczb. Otrzymujemy wtedy algorytm, w którym wykonuje się 8 porównań. Możliwy jest jednak algorytm, w którym wykonuje się co najwyżej 7 operacji. 4
Dotychczas przedstawione algorytmy były uniwersalne jedynie w tym sensie, że danymi w nich mogły być dowolne liczby. Ich liczba była jednak z góry ustalona. Obecnie prezentowane algorytmy powinny działać poprawnie zarówno na zbiorze 3 jak i 100 000 elementów. Wynik działania algorytmu będzie najczęściej zależeć od wszystkich danych w zbiorze - jego określenie będzie więc związane z koniecznością przejrzenia całego zbioru i wykonania podobnych operacji na wszystkich elementach zbioru. W algorytmie takiemu działaniu odpowiadać będzie iteracja (powtarzanie) pewnych operacji lub całych fragmentów obliczeń. Dla uproszczenia rozumowania będziemy zajmować się zbiorami, których elementami są liczby. Mogą one być traktowane jako reprezentacja innych danych. Mówiąc o zbiorach danych będziemy mieli na uwadze zbiory liczb. 5
Dla każdej danej w zbiorze można określić częstość, wielkość określającą, ile razy powtarza się ona w tym zbiorze. Rozpiętością zbioru jest różnica pomiędzy największą i najmniejszą liczbą występującą w tym zbiorze. Najmniejszą liczbę w zbiorze nazywamy minimum zbioru a największa maksimum zbioru. Miarami centralności danych w zbiorze, zwanymi również statystykami, są: średnia, mediana, modalna. Średnia jest równa sumie wszystkich danych podzielona przez ich liczbę. Mediana jest środkową wartością zbioru w tym sensie, że jeśli zbiór jest uporządkowany, to występuje ona w środku tego uporządkowania. Modalna jest najczęściej występującą daną w zbiorze. 6
Istotnym zagadnieniem jest reprezentacja w algorytmach zbiorów o nie ustalonej z góry liczbie elementów. Podczas prowadzenia obliczeń musimy mieć pewność, że: żaden z elementów zbioru nie został pominięty przez algorytm, obliczenia nie będą się powtarzały w nieskończoność. Zakładamy, że zbiór danych, dla których będą wykonywane obliczenia: jest czytany z klawiatury, znajduje się w tablicy lub na taśmie. Opis algorytmu powinien być poprzedzony dyskusją, w jaki sposób są przechowywane dane. Od sposobu przechowywania danych zależy bowiem postać algorytmu. Taśmą będziemy nazywać ciąg komórek wypełnionych liczbami. 7
Bez względu na sposób reprezentowania zbioru powinniśmy wiedzieć, ile zawiera on elementów, czyli jaka jest jego moc. Istnieją dwa podstawowe sposoby określenia zbioru (jego mocy): na początku danego ciągu danych podajemy, ile elementów zawiera zbiór, a następnie wymieniamy jego elementy (wiemy wówczas, ile należy wykonać dokładnie działań na wszystkich elementach zbioru); na końcu zbioru umieszczamy wyróżniony element, który nie należy do tego zbioru - nazywamy go wartownikiem; jego zadaniem jest pilnowanie, by żadne obliczenia dokonywane na elementach zbioru nie wykroczyły poza jego elementy (dla zbioru liczb dodatnich wartownikiem może być każda liczba ujemna). Rola wartownika może być bardziej ogólna, niż tylko zakończenie zbioru. Najprostsze pytanie, jakie można postawiæ w odniesieniu do zbioru danych brzmi: czy zawiera on poszukiwany przez nas element. Jeśli nie posiadamy żadnych informacji o uporządkowaniu elementów zbioru najprostszą metodą udzielenia odpowiedzi na to pytanie jest sprawdzenie element po elemencie, czy któryś z elementów nie jest równy poszukiwanemu. Jest to przeszukiwanie liniowe. 8
W przeszukiwaniu wykorzystamy wartownika w następujący sposób: jeżeli y jest poszukiwanym elementem zbioru, to to dołączamy do tego zbioru na jego końcu właśnie ten element. W takiej sytuacji przeszukiwanie zawsze zakończy się odnalezieniem elementu równego y! Należy jedynie sprawdzić, czy znajduje się on w ciągu danych czy tez może na dołączonej pozycji. W pierwszym wypadku przeszukiwany zbiór zawiera element y, a w drugim y nie należy do tego zbioru. Dołączony do zbioru element odgrywa rolę wartownika - nie musimy sprawdzać, czy przeglądanie objęło cały zbiór czy nie, zatrzyma się ono bowiem zawsze na szukanym elemencie. W zbiorze poszukiwać możemy elementu o danej wartości, elementu o wyróżnionej właściwości (np. największego). 9
Algorytm przeszukiwania liniowego z wartownikiem Dane: Zbiór elementów w postaci ciągu; y - poszukiwany element Wyniki: Jeśli y należy do zbioru podaj jego pozycję, jeśli nie zasygnalizuj jego brak w zbiorze danych. Krok 1 {Utworzenie wartownika na końcu ciągu} Utwórz na końcu ciągu wartownika i umieść w nim y. Krok 2 Dla kolejnego element z w danym ciągu jeśli z=y to przejdź do następnego kroku, w przeciwnym razie powtórz ten krok. Krok 3 Jeżeli z jest wartownikiem, to zbiór nie zawiera y, w przeciwnym wypadku z wskazuje pierwsze wystąpienie y w ciągu. Znalezienie czegokolwiek trwa na ogół krócej, niż stwierdzenie jego braku. 10
Algorytm przeszukiwania liniowego z wartownikiem - wersja z tablicą Dane: Zbiór elementów dany w tablicy a[k..l], gdzie k<=l; y - poszukiwany element. Wynik: Takie s (k<=s<=l), że a s =y lub s=-1 jeśli y<>a i dla każdego i (k<=i<=l) Krok 1 a l+1 :=y {utworzenie wartownika na końcu ciągu} Krok 2 Dla s=k,, l, l+1, jeśli a s =y to wykonaj następny krok. Krok 3 Jeśli s=l+1 to zbiór nie zawiera elementu y i przyjmujemy s=-1; w przeciwnym wypadku a s =y. Średnia ze zbioru liczb jest równa liczbie elementów podzielonej przez ich liczbę. Algorytm obliczania średniej z liczb dodatnich wprowadzanych z klawiatury będzie zależeć od sposobu czytania danych: zbiór danych jest poprzedzony ich liczbą w zbiorze, zbiór danych jest zakończony wartownikiem. Przyjmujemy, że suma elementów zbioru, który jest pusty, wynosi zero. 11
12