You are on page 1of 37

IDZ DO

PRZYKADOWY ROZDZIA
SPIS TRECI

KATALOG KSIEK
KATALOG ONLINE

Algorytmy. Od podstaw
Autorzy: Simon Harris, James Ross
Tumaczenie: Andrzej Grayski
ISBN: 83-246-0372-7
Tytu oryginau: Beginning Algorithms
Format: B5, stron: 610

ZAMW DRUKOWANY KATALOG

TWJ KOSZYK
DODAJ DO KOSZYKA

CENNIK I INFORMACJE
ZAMW INFORMACJE
O NOWOCIACH
ZAMW CENNIK

CZYTELNIA
FRAGMENTY KSIEK ONLINE

Wprowadzenie do problematyki algorytmw i struktur danych


Badanie zoonoci algorytmw
Analiza i implementacja algorytmw
Zasady testowania kodu
Algorytmy le u podstaw programowania. Zasady rozwizywania typowych problemw
programistycznych, opisane w postaci blokowej lub za pomoc uniwersalnego
pseudokodu, s wykorzystywane codziennie przez tysice informatykw na caym
wiecie. Waciwe zrozumienie zarwno samych algorytmw, jak i zasad ich
stosowania w praktyce, jest kluczem do tworzenia wydajnych aplikacji. Umiejtno
oceny efektywnoci i zoonoci algorytmw przyda si rwnie przy wyborze
najlepszego rozwizania okrelonego problemu.
Ksika Algorytmy. Od podstaw przedstawia podstawowe zagadnienia zwizane
z algorytmami. Dziki niej nauczysz si wyznacza zoono obliczeniow algorytmw
i implementowa algorytmy w programach. Poznasz algorytmy sortowania,
przeszukiwania i przetwarzania danych. Dowiesz si, czym s testy jednostkowe
i dlaczego ich stosowanie jest tak wane podczas tworzenia oprogramowania.
W ksice omwiono m.in. nastpujce zagadnienia:
Testy jednostkowe i biblioteka JUnit
Iteracja i rekurencja
Kolejki FIFO
Listy i stosy
Algorytmy sortowania
Binarne wyszukiwanie i zastpowanie
Zbiory, mapy i drzewa wyszukiwawcze
Wyszukiwanie tekstu

Wydawnictwo Helion
ul. Chopina 6
44-100 Gliwice
tel. (32)230-98-63
e-mail: helion@helion.pl

Poznaj sprawdzone i powszechnie uywane algorytmy


i zastosuj je w swoich aplikacjach

O autorach ................................................................................................................................................... 9
Podzikowania ............................................................................................................................................11
Wprowadzenie ...........................................................................................................................................13
Rozdzia 1. Zaczynamy .............................................................................................................................. 23
Czym s algorytmy? ..................................................................................................... 23
Co to jest zoono algorytmu? .................................................................................... 26
Porwnywanie zoonoci i notacja duego O ............................................................... 27
Zoono staa O(1) ........................................................................................... 29
Zoono liniowa O(N) ........................................................................................ 29
Zoono kwadratowa O(N2) ............................................................................... 30
Zoono logarytmiczna O(log N) i O(N log N) ....................................................... 31
Zoono rzdu silni O(N!) .................................................................................. 32
Testowanie moduw .................................................................................................... 32
Czym jest testowanie moduw? .............................................................................. 33
Dlaczego testowanie moduw jest wane? ............................................................... 35
Biblioteka JUnit i jej wykorzystywanie ........................................................................ 35
Programowanie sterowane testami ........................................................................... 38
Podsumowanie ............................................................................................................ 39

Rozdzia 2. Iteracja i rekurencja ..............................................................................................................41


Wykonywanie oblicze .................................................................................................. 42
Przetwarzanie tablic ..................................................................................................... 44
Iteratory jako uoglnienie przetwarzania tablicowego ................................................. 45
Rekurencja .................................................................................................................. 62
Przykad rekurencyjne drukowanie drzewa katalogw ............................................. 64
Anatomia algorytmu rekurencyjnego ......................................................................... 68
Podsumowanie ............................................................................................................ 69
wiczenia .................................................................................................................... 70

Algorytmy. Od podstaw
Rozdzia 3. Listy .........................................................................................................................................71
Czym s listy? ............................................................................................................. 71
Testowanie list ............................................................................................................ 74
Implementowanie list ................................................................................................... 86
Lista tablicowa ....................................................................................................... 87
Lista wizana ......................................................................................................... 95
Podsumowanie .......................................................................................................... 104
wiczenia .................................................................................................................. 104

Rozdzia 4. Kolejki ....................................................................................................................................105


Czym s kolejki? ........................................................................................................ 105
Operacje kolejkowe ............................................................................................... 106
Interfejs kolejki ..................................................................................................... 107
Kolejka FIFO .............................................................................................................. 107
Implementowanie kolejki FIFO ................................................................................ 111
Kolejki blokujce ........................................................................................................ 113
Przykad symulacja centrum obsugi ........................................................................ 117
Uruchomienie aplikacji .......................................................................................... 127
Podsumowanie .......................................................................................................... 128
wiczenia .................................................................................................................. 129

Rozdzia 5. Stosy .......................................................................................................................................131


Czym s stosy? ......................................................................................................... 131
Testy ........................................................................................................................ 133
Implementacja ........................................................................................................... 136
Przykad implementacja operacji Cofnij/Powtrz .................................................... 140
Testowanie cofania/powtarzania ............................................................................ 141
Podsumowanie .......................................................................................................... 149

Rozdzia 6. Sortowanie proste algorytmy .........................................................................................151


Znaczenie sortowania ................................................................................................. 151
Podstawy sortowania .................................................................................................. 152
Komparatory .............................................................................................................. 153
Operacje komparatora ........................................................................................... 153
Interfejs komparatora ............................................................................................ 154
Niektre komparatory standardowe ........................................................................ 154
Sortowanie bbelkowe ............................................................................................... 159
Interfejs ListSorter ................................................................................................ 161
Abstrakcyjna klasa testowa dla sortowania list ........................................................ 161
Sortowanie przez wybieranie ....................................................................................... 165
Sortowanie przez wstawianie ...................................................................................... 170
Stabilno sortowania ................................................................................................ 173
Porwnanie prostych algorytmw sortowania ................................................................ 175
CallCountingListComparator .................................................................................. 176
ListSorterCallCountingTest .................................................................................... 177
Jak interpretowa wyniki tej analizy? ....................................................................... 180
Podsumowanie .......................................................................................................... 180
wiczenia .................................................................................................................. 181

Spis treci

Rozdzia 7. Sortowanie zaawansowane .................................................................................................183


Sortowanie metod Shella .......................................................................................... 183
Sortowanie szybkie .................................................................................................... 189
Komparator zoony i jego rola w zachowaniu stabilnoci sortowania .............................. 195
Sortowanie przez czenie ........................................................................................... 198
czenie list ......................................................................................................... 198
Algorytm Mergesort ............................................................................................... 199
Porwnanie zaawansowanych algorytmw sortowania ................................................... 205
Podsumowanie .......................................................................................................... 208
wiczenia .................................................................................................................. 209

Rozdzia 8. Kolejki priorytetowe .............................................................................................................211


Kolejki priorytetowe .................................................................................................... 212
Prosty przykad kolejki priorytetowej ....................................................................... 212
Wykorzystywanie kolejek priorytetowych ....................................................................... 215
Kolejka priorytetowa oparta na licie nieposortowanej ............................................. 218
Kolejka priorytetowa wykorzystujca list posortowan ............................................ 220
Kolejki priorytetowe o organizacji stogowej .............................................................. 222
Porwnanie implementacji kolejek priorytetowych ......................................................... 229
Podsumowanie .......................................................................................................... 233
wiczenia .................................................................................................................. 233

Rozdzia 9. Binarne wyszukiwanie i wstawianie .................................................................................235


Wyszukiwanie binarne ................................................................................................ 235
Dwa sposoby realizacji wyszukiwania binarnego ...................................................... 238
Interfejs wyszukiwania binarnego ........................................................................... 238
Iteracyjna wyszukiwarka binarna ............................................................................. 244
Ocena dziaania wyszukiwarek ............................................................................... 247
Wstawianie binarne .................................................................................................... 253
Inserter binarny .................................................................................................... 254
Porwnywanie wydajnoci ...................................................................................... 257
Podsumowanie .......................................................................................................... 261

Rozdzia 10. Binarne drzewa wyszukiwawcze ....................................................................................263


Binarne drzewa wyszukiwawcze ................................................................................... 264
Minimum ............................................................................................................. 265
Maksimum ........................................................................................................... 265
Nastpnik ............................................................................................................ 265
Poprzednik ........................................................................................................... 266
Szukanie .............................................................................................................. 266
Wstawianie .......................................................................................................... 268
Usuwanie ............................................................................................................. 269
Trawersacja in-order .............................................................................................. 272
Trawersacja pre-order ............................................................................................ 272
Trawersacja post-order .......................................................................................... 273
Wywaanie drzewa ................................................................................................ 273
Testowanie i implementowanie binarnych drzew wyszukiwawczych ................................. 275
Ocena efektywnoci binarnego drzewa wyszukiwawczego .............................................. 299
Podsumowanie .......................................................................................................... 302
wiczenia .................................................................................................................. 303

Algorytmy. Od podstaw
Rozdzia 11. Haszowanie ..........................................................................................................................305
Podstawy haszowania ................................................................................................. 305
Praktyczna realizacja haszowania ................................................................................ 311
Prbkowanie liniowe ............................................................................................. 314
Porcjowanie .......................................................................................................... 321
Ocena efektywnoci tablic haszowanych ...................................................................... 326
Podsumowanie .......................................................................................................... 332
wiczenia .................................................................................................................. 332

Rozdzia 12. Zbiory ..................................................................................................................................333


Podstawowe cechy zbiorw ......................................................................................... 333
Testowanie implementacji zbiorw ......................................................................... 336
Zbir w implementacji listowej .................................................................................... 342
Zbir haszowany ........................................................................................................ 344
Zbir w implementacji drzewiastej ............................................................................... 349
Podsumowanie .......................................................................................................... 356
wiczenia .................................................................................................................. 356

Rozdzia 13. Mapy ....................................................................................................................................357


Koncepcja i zastosowanie map ................................................................................... 357
Testowanie implementacji map ................................................................................... 362
Mapa w implementacji listowej .................................................................................... 369
Mapa w implementacji haszowanej .............................................................................. 373
Mapa w implementacji drzewiastej .............................................................................. 377
Podsumowanie .......................................................................................................... 384
wiczenia .................................................................................................................. 384

Rozdzia 14. Ternarne drzewa wyszukiwawcze ..................................................................................385


Co to jest drzewo ternarne? ........................................................................................ 385
Wyszukiwanie sowa .............................................................................................. 386
Wstawianie sowa ................................................................................................. 389
Poszukiwanie prefiksu ........................................................................................... 391
Dopasowywanie wzorca ......................................................................................... 392
Drzewa ternarne w praktyce ........................................................................................ 395
Przykad zastosowania rozwizywanie krzywek ....................................................... 409
Podsumowanie .......................................................................................................... 412
wiczenie .................................................................................................................. 412

Rozdzia 15. B-drzewa .............................................................................................................................413


Podstawowe wasnoci B-drzew ................................................................................... 413
Praktyczne wykorzystywanie B-drzew ............................................................................ 419
Podsumowanie .......................................................................................................... 431
wiczenie .................................................................................................................. 431

Rozdzia 16. Wyszukiwanie tekstu .........................................................................................................433


Interfejs wyszukiwarki acuchw ................................................................................ 433
Zestaw testowy dla wyszukiwarki acuchw ................................................................ 435
Prymitywny algorytm wyszukiwania ............................................................................... 438
Algorytm Boyera-Moorea ............................................................................................ 441
Tworzenie testw dla algorytmu Boyera-Moorea ...................................................... 443
Implementowanie algorytmu Boyera-Moorea .......................................................... 444

Spis treci

Iterator dopasowywania wzorca ................................................................................... 447


Porwnanie efektywnoci wyszukiwania ....................................................................... 449
Pomiar efektywnoci ............................................................................................. 450
Wyniki eksperymentu ............................................................................................ 454
Podsumowanie .......................................................................................................... 454

Rozdzia 17. Dopasowywanie acuchw ..............................................................................................457


Soundex .................................................................................................................... 457
Odlego Levenshteina dwch sw ............................................................................ 468
Podsumowanie .......................................................................................................... 477

Rozdzia 18. Geometria obliczeniowa .....................................................................................................479


Podstawowe pojcia geometryczne .............................................................................. 479
Wsprzdne i punkty ............................................................................................ 479
Linie .................................................................................................................... 481
Trjkty ................................................................................................................ 481
Znajdowanie punktu przecicia dwch linii .............................................................. 482
Punkt przecicia dwch linii ........................................................................................ 485
Znajdowanie pary najbliszych punktw ........................................................................ 499
Podsumowanie .......................................................................................................... 510
wiczenia .................................................................................................................. 510

Rozdzia 19. Optymalizacja pragmatyczna .............................................................................................511


Kiedy optymalizowanie ma sens? ................................................................................ 511
Profilowanie ............................................................................................................... 513
Przykadowy program FileSortingHelper ........................................................................ 514
Profilowanie za pomoc moduu hprof .................................................................... 517
Profilowanie za pomoc JMP .................................................................................. 520
Istota optymalizacji .................................................................................................... 522
Optymalizacja w praktyce ............................................................................................ 523
Podsumowanie .......................................................................................................... 530

Dodatek A Zalecana literatura uzupeniajca ........................................................................................531


Dodatek B Wybrane zasoby internetowe ..............................................................................................533
Dodatek C Literatura cytowana .............................................................................................................535
Dodatek D Odpowiedzi do wicze .........................................................................................................537
Skorowidz ...............................................................................................................................................585

Opisywane w poprzednich rozdziaach struktury danych peni fundamentaln rol w tworzonych aplikacjach jako rodki organizujce przetwarzanie ogromnych iloci danych. W szczeglnoci sortowanie danych wedug pewnego kryterium stanowi nieodczny element wielu
algorytmw, w tym algorytmw opisywanych w dalszych rozdziaach niniejszej ksiki.
Jednoczenie staje si ono czsto wskim gardem wydajnoci aplikacji, nic wic dziwnego,
e sortowanie danych rozmaitych typw byo przedmiotem intensywnych bada w ostatnich dziesicioleciach i nadal stanowi jeden z kluczowych punktw zainteresowa informatyki. W niniejszym rozdziale omawiamy trzy proste algorytmy sortowania, atwe w implementacji, lecz przydatne raczej dla niewielkich iloci danych, a to ze wzgldu na zoono
kwadratow O(N2) (N jest liczb elementw w sortowanym zestawie). Bardziej wydajnym
i jednoczenie bardziej skomplikowanym algorytmom sortowania powicony bdzie
rozdzia 7.
W niniejszym rozdziale omawiamy:
n

znaczenie sortowania w tworzonych aplikacjach i nie tylko,

rol komparatorw w konstrukcji i przetwarzaniu struktur danych,

algorytm sortowania bbelkowego,

algorytm sortowania przez wybieranie,

algorytm sortowania przez wstawianie,

wasno stabilnoci sortowania,

zalety i wady prostych metod sortowania.

Przegldajc na co dzie ksik telefoniczn, spis teleadresowy itp., najczciej nie uwiadamiamy sobie, i wykorzystujemy fakt ich posortowania. Szukajc okrelonego nazwiska
czy firmy, po prostu prbujemy zgadn, w ktrym miejscu spisu moemy si go spodziewa,

152

Algorytmy. Od podstaw
i ju po kilku takich prbach trafiamy na dan stron, na ktrej w cigu kilku sekund odnajdujemy to, czego szukamy (bd stwierdzamy, e taki to a taki abonent w spisie teleadresowym nie figuruje). Wyobramy sobie teraz, e taki spis teleadresowy nie jest posortowany abonenci wystpuj w nim w kolejnoci przypadkowej1. Trzeba mie duo
dobrej woli i determinacji, by w ogle podj si prby znalezienia w nim czegokolwiek
lub kogokolwiek prby raczej z gry skazanej na niepowodzenie. Wiele zbiorw danych
byoby zupenie bezuytecznych, gdyby nie zostay posortowane wedug pewnego uytecznego kryterium dotyczy to nie tylko nazwisk czy nazw w spisie teleadresowym, lecz take
np. ksiek na pkach bibliotecznych. Jako e czsto zbiory te posortowane s a priori,
uwaamy to za co naturalnego i w ogle o sortowaniu nie mylimy. W przypadku komputerowego przetwarzania danych jest zupenie inaczej: trudno oczekiwa, e uytkownik
aplikacji dostarcza bdzie dane w kolejnoci posortowanej, a w kadym razie byoby czym
kuriozalnym wymaga od niego czegokolwiek, co znacznie efektywniej moe za niego wykona komputer. Sortowanie rozmaitych danych staje si wic nieodczn czynnoci
wielu aplikacji, a dobra znajomo rnych metod sortowania jest warunkiem wykonywania tej czynnoci w sposb efektywny.

Warunkiem wstpnym moliwoci posortowania danych wedug pewnego kryterium jest


istnienie struktury zdolnej przechowywa elementy tych danych w okrelonej kolejnoci.
Jak widzielimy w rozdziale 3., to wanie listy s strukturami zachowujcymi (wzgldn)
kolejno wstawianych elementw interfejs List nie zawiera metod zmieniajcych t
kolejno w sposb bezporedni, zmiana pozycji elementu w licie nie jest moliwa bez jego
usunicia i ponownego wstawienia.
Kady algorytm sortowania listy elementw opiera si na dwch fundamentalnych operacjach:
n

porwnywaniu elementw w celu stwierdzenia, czy ich wzgldna kolejno


w licie zgodna jest z kryterium sortowania,

przesuwaniu elementw na pozycje wyznaczane przez kryterium sortowania.

Zalety i wady danego algorytmu sortowania wynikaj przede wszystkim z tego, ile wymienionych wyej operacji naley wykona w celu posortowania okrelonego zbioru danych i jak
efektywna jest kada z tych operacji. Porwnywanie elementw jest czynnoci znacznie
mniej oczywist, ni mogoby si to w pierwszej chwili wydawa; w kolejnym podrozdziale
omawiamy wynikajc z niego koncepcj komparatora. Dokadny opis wykorzystywanych
operacji listowych get(), set(), insert() i delete() znajdzie Czytelnik w rozdziale 3.

Albo w kolejnoci wyznaczonej przez kryterium nieznane uytkownikowi, na przykad


w kolejnoci zgoszenia swych danych do wydawcy przyp. tum.

Rozdzia 6. n Sortowanie proste algorytmy

153

W jzyku Java i w wikszoci innych jzykw programowania porwnywanie wartoci


dwch zmiennych cakowitoliczbowych jest czynnoci niewymagajc komentarza:
int x, y;
...
if (x < y) {
...
}

Podobnie ma si rzecz w przypadku podstawowych (primitive) typw danych, lecz w miar


postpujcej komplikacji struktur danych porwnywanie ich elementw (obiektw) szybko
traci sw oczywisto. Wyobramy sobie na przykad list plikw znajdujcych si w pewnym
katalogu: list t mona (stosownie do rnych potrzeb) sortowa wedug rozmaitych kryteriw nazwy, rozszerzenia, rozmiaru, daty utworzenia, daty ostatniej modyfikacji itp.
Wane jest wic oddzielenie kryterium sortowania elementw od samej czynnoci sortowania. Mechanizm narzucajcy na list obiektw pewne kryterium porzdkujce nosi nazw komparatora; dla danej listy (na przykad wspomnianej listy plikw) okreli mona
kilka rnych komparatorw wyraajcych rozmaite kryteria uporzdkowania. Dziki temu
sortowanie listy wedug rnych kryteriw nazwy pliku, jego rozszerzenia, rozmiaru itp.
da si zrealizowa w sposb jednolity, za pomoc tego samego algorytmu sortowania.
Wspomniane oddzielenie kryterium sortowania od samego sortowania jest przykadem bardziej oglnej koncepcji projektowej, zwanej rozdzielaniem zagadnie (separation of concerns). Umoliwia ono rozszerzanie uytecznoci samego algorytmu dziki zastosowaniu
rozmaitych wtyczek (plugins) nawet takich, ktre trudno byoby sobie wyobrazi w momencie implementowania samego algorytmu. Wanie komparatory s przykadem takich
wtyczek dla algorytmw sortowania. Mona take spojrze na ca spraw z drugiej strony:
zastosowanie tego samego komparatora do rnych algorytmw sortowania umoliwia miarodajne porwnywanie wydajnoci tych algorytmw.

Operacje komparatora
Komparator wykonuje tylko jedn operacj jest ni okrelenie wzgldnej kolejnoci
dwch porwnywanych obiektw. Zalenie od tego, czy pierwszy z wymienionych obiektw jest mniejszy, rwny lub wikszy od drugiego (w sensie przyjtego kryterium porwnywania), wynikiem tej operacji jest (odpowiednio) warto ujemna, zero lub warto dodatnia. Jeli typ ktregokolwiek z wymienionych obiektw wyklucza moliwo porwnywania
go z innymi obiektami, prba wykonania porwnania powoduje wystpienie wyjtku ClassCastException.

154

Algorytmy. Od podstaw

Interfejs komparatora
Jedyna operacja komparatora przekada si na jedyn metod interfejsu Comparator, okrelajc wzgldn relacj (porzdek) midzy obiektami okrelonymi przez jej argumenty:
public interface Comparator {
public int compare(Object left, Object right);
}

Argumenty metody nieprzypadkowo okrelone zostay jako lewy (left) i prawy (right),
mog by bowiem utosamiane z (odpowiednio) lewym i prawym argumentem operatora
porwnania metoda compare() w istocie stanowi uoglnienie operatora porwnania dla
typw podstawowych jzyka. Zalenie od tego, czy obiekt left jest (w sensie przyjtego
kryterium porwnywania) mniejszy od obiektu right, rwny mu lub od niego wikszy,
metoda zwraca (odpowiednio) warto ujemn (zwykle 1, cho niekoniecznie), zero (koniecznie) lub warto dodatni (zwykle 1, cho niekoniecznie).

Niektre komparatory standardowe


Oprcz nieograniczonych wrcz moliwoci definiowania komparatorw przez programist
istnieje w jzyku Java kilka komparatorw standardowych w duym stopniu upraszczajcych
tworzenie kodu aplikacji. Kady z nich jest prosty pod wzgldem koncepcyjnym i wielce
uyteczny w konstrukcji wielu zoonych algorytmw prezentowanych w niniejszej ksice.

Komparator naturalny
W wielu typach danych, szczeglnie typach podstawowych, jak acuchy czy liczby cakowite, zdefiniowane jest a priori uporzdkowanie naturalne: 1 poprzedza 2, A poprzedza B, B
poprzedza C itp. Komparator narzucajcy taki wanie naturalny porzdek nazywamy (jakeby inaczej) komparatorem naturalnym. Jak pokaemy za chwil, dla danych okrelonego
typu zdefiniowa mona ich naturalne uporzdkowanie, bazujc na konwencjach obowizujcych w jzyku Java umoliwiajcym to rodkiem jest interfejs Comparable.

Interfejs Comparable
Interfejs Comparable posiada tylko jedn metod:
public interface Comparable {
public int compareTo(Object other);
}

zwracajc podobnie jak metoda compare() interfejsu Comparator warto ujemn,


zero lub warto dodatni zalenie od tego, czy obiekt stanowicy podmiot wywoania metody
jest (odpowiednio) mniejszy, rwny lub wikszy od obiektu reprezentowanego przez parametr other. Zasadnicza rnica midzy metod compare() a metod compareTo() polega na
tym, i ta pierwsza porwnuje ze sob dwa wskazane obiekty, podczas gdy druga porwnuje dany obiekt z innym obiektem.

Rozdzia 6. n Sortowanie proste algorytmy

155

Jest wic jasne, e aby zdefiniowa naturalny porzdek w stosunku do wartoci danej klasy,
naley zaimplementowa w tej klasie interfejs Comparable. Przykadowo dla rekordw zawierajce dane pracownikw za uporzdkowanie naturalne mona przyj uporzdkowanie
wedug nazwiska i imienia. Koncepcja ta stanowi uoglnienie operatorw <, = i > na zoone
typy danych i faktycznie wiele powszechnie uywanych klas z pakietu java.lang implementuje interfejs Comparable.
Gdy wyobrazimy sobie funkcjonowanie komparatora naturalnego (ktrego klas nazwiemy
NaturalComparator), szybko stanie si jasne, i naley go przetestowa w kontekcie trzech

przypadkw: porwnania obiektu mniejszego z wikszym, wikszego z mniejszym


oraz dwch rwnych obiektw. Aby uatwi sobie zadanie, wykorzystamy fakt, e interfejs Comparable zdefiniowany jest standardowo m.in. dla acuchw jzyka Java.

sprbuj sam Testowanie komparatora naturalnego


Rozpoczniemy od konfiguracji, w ktrej metoda compare() komparatora naturalnego powinna zwrci warto ujemn:
public void testLessThan() {
assertTrue(NaturalComparator.INSTANCE.compare("A", "B") < 0);
}

Zamieniajc miejscami porwnywane obiekty, otrzymamy konfiguracj, w ktrej metoda


compare() powinna zwrci warto dodatni:
public void testGreaterThan() {
assertTrue(NaturalComparator.INSTANCE.compare("B", "A") > 0);
}

Przy porwnywaniu dwch identycznych wartoci metoda compare() powinna zwrci 0:


public void testEqualTo () {
assertTrue(NaturalComparator.INSTANCE.compare("A", "A") = 0);
}

Jak to dziaa?
Dla kadego z trzech moliwych przypadkw relacji midzy porwnywanymi obiektami
(mniejszy, rwny, wikszy) zdefiniowano osobny przypadek testowy, wykonujcy oczywiste
porwnanie acuchw jednoznakowych. Wszystkie przypadki testowe bazuj na zaoeniu, e istnieje tylko jedna statyczna instancja klasy NaturalComparator i nie naley tworzy innych jej instancji.

sprbuj sam Implementowanie komparatora naturalnego


Poniewa klasa NaturalComparator nie przechowuje adnych informacji o stanie, wystarczajce jest istnienie tylko jednej, publicznie dostpnej jej instancji:

156

Algorytmy. Od podstaw
public final class NaturalComparator implements Comparator {
/** jedyna, publicznie dostpna instancja komparatora */
public static final NaturalComparator INSTANCE = new NaturalComparator();

/**
* konstruktor prywatny, nie jest moliwe samodzielne tworzenie instancji
*/
private NaturalComparator() {
}
...

Aby uniemoliwi samodzielne tworzenie kolejnych instancji, uczyniono konstruktor klasy


prywatnym, a wic niewidocznym na zewntrz niej. Ponadto sama klasa oznaczona zostaa
jako finalna (final) w celu zapobieenia jej (by moe bdnemu) rozszerzaniu.
Poniewa metoda compare() komparatora naturalnego implementowana jest na bazie interfejsu Comparable, zasadnicza jej czynno scedowana zostaa na jej argumenty, co czyni jej
implementacj niemal banaln:
public int compare(Object left, Object right) throws ClassCastException {
assert left != null : "nie okrelono lewego obiektu ";
return ((Comparable) left).compareTo(right);
}

Po upewnieniu si, e lewy argument nie jest argumentem pustym, nastpuje jego rzutowanie na instancj interfejsu Comparable i wywoanie metody compareTo() tego interfejsu z prawym obiektem jako argumentem.
Rzutujc obiekt left na instancj interfejsu Comparable nie sprawdzamy, czy rzutowanie to
jest wykonalne, tzn. czy typ tego obiektu nie wyklucza wykonywania jego porwna z innymi obiektami. Sprawdzenie takie jest niepotrzebne, bowiem interfejs Comparator dopuszcza wystpowanie wyjtku ClassCastException w sytuacji, gdy wymieniony wyej warunek
nie jest speniony.

Jak to dziaa?
Klasa NaturalComparator skonstruowana zostaa w celu porwnywania dwch obiektw
implementujcych interfejs Comparable. Interfejs ten implementowany jest standardowo
przez wiele klas Javy i oczywicie mona go implementowa ad hoc w klasach definiowanych samodzielnie. Implementacja taka polega kadorazowo na zrzutowaniu lewego argumentu na instancj interfejsu Comparable i wywoaniu na jego rzecz metody compareTo()
z prawym argumentem jako parametrem. Wszelka logika porwnywania nie jest w tej
implementacji widoczna, skrywa si bowiem cakowicie w implementacji metody compareTo().

Komparator odwrotny
Zdarza si, e majc zdefiniowane pewne uporzdkowanie wartoci jakiego typu, chcielibymy posortowa te wartoci w kolejnoci dokadnie odwrotnej ni wynikajca z tego
uporzdkowania, na przykad wypisa nazwy plikw pewnego katalogu w kolejnoci od-

Rozdzia 6. n Sortowanie proste algorytmy

157

wrotnej do kolejnoci alfabetycznej. Trywialnym sposobem wykonania tego zadania jest


zamiana znacze obiektw stanowicych argumenty wywoania metody compare() komparatora NaturalComparator:
public int compare(Object left, Object right) throws ClassCastException {
assert right != null : "nie okrelono prawego obiektu ";
return ((Comparable) right).compareTo(left);
}

Po upewnieniu si, e lewy argument nie jest argumentem pustym, nastpuje wywoanie
jego metody compareTo() z prawym argumentem jako parametrem wywoania.
Mimo i to dorane rozwizanie sprawdza si niele w tym szczeglnym przypadku, jest
mao uniwersalne, bowiem dla bardziej zoonych struktur danych, jak lista plikw czy lista
danych pracowniczych, wymaga definiowania dwch komparatorw, po jednym dla kadego
kierunku uporzdkowania.
Znacznie bardziej eleganckie rozwizanie, ktre za chwil zaprezentujemy, polega na odwrceniu kierunku wskazanego komparatora poprzez jego udekorowanie (otoczenie) innym
komparatorem, w wyniku czego otrzymuje si komparator zwany komparatorem odwrotnym.
Dla kadego typu danych wystarczy wwczas zdefiniowa tylko jeden komparator i w razie
potrzeby odwraca wyznaczane przez niego uporzdkowanie w sposb uniwersalny, za
pomoc opisanego odwracania.

sprbuj sam Testowanie komparatora odwrotnego


Podobnie jak w przypadku komparatora NaturalComparator, tak i w przypadku komparatora
odwrotnego ktry nazwiemy ReverseComparator przetestowa musimy trzy moliwe
przypadki relacji midzy porwnywanymi obiektami. Dekorowanym komparatorem, ktrego
kierunek bdziemy odwraca, bdzie przy tym sam komparator naturalny NaturalComparator.
Jeli lewy argument metody compare() komparatora NaturalComparator jest mniejszy od jej
prawego argumentu, metoda ta powinna zwrci warto ujemn. Jeeli jednak na bazie
komparatora NaturalComparator stworzymy komparator odwrotny, to jego metoda compare()
zwrci musi w takiej sytuacji warto dodatni:
public void testLessThanBecomesGreaterThan() {
ReverseComparator comparator =
new ReverseComparator(NaturalComparator.INSTANCE);
assertTrue(comparator.compare("A", "B") > 0);
}

Analogicznie, jeli lewy argument komparatora odwrotnego jest wikszy ni prawy, metoda
compare() tego komparatora powinna zwrci warto ujemn:
public void testGreaterThanBecomesLessThan() {
ReverseComparator comparator =
new ReverseComparator(NaturalComparator.INSTANCE);
assertTrue(comparator.compare("B", "A") < 0);
}

158

Algorytmy. Od podstaw
W przypadku porwnywania identycznych argumentw nic si nie zmienia, zarwno dla
komparatora oryginalnego, jak i odwrotnego metoda compare() powinna zwrci 0:
public void testEqualsRemainstnchanged() {
ReverseComparator comparator =
new ReverseComparator(NaturalComparator.INSTANCE);
assertTrue(comparator.compare("A", "A") == 0);
}

Jak to dziaa?
Na bazie komparatora naturalnego NaturalComparator tworzony jest komparator odwrotny
ReverseComparator, ktrego dziaanie (czyli wyniki porwna) powinno by dokadnie odwrotne w stosunku do pierwowzoru. W szczeglnoci acuch A powinien by uznany za
wikszy od acucha B i vice versa acuch B powinien zosta uznany za mniejszy od acucha A. W przypadku porwnywania dwch identycznych obiektw komparator odwrotny take powinien uzna je za identyczne.

sprbuj sam Implementowanie komparatora odwrotnego


Implementacja komparatora ReverseComparator skada si z niewielu linii kodu:
package com.wrox.algorithms.sorting;
public class ReverseComparator implements Comparator {
private final Comparator _comparator;
public ReverseComparator(Comparator comparator) {
assert comparator != null : "nie okrelono oryginalnego komparatora";
_comparator = comparator;
}
...
}

W konstruktorze klasy przekazywany jest oryginalny komparator, ktrego dziaanie ulec


ma odwrceniu; jest on zapamitywany w prywatnej zmiennej _comparator.
Pozostaje tylko zaimplementowanie metody compare() jedynej metody implementowanego interfejsu Comparator:
public int compare(Object left, Object right) throws ClassCastException {
return _comparator.compare(right, left);
}

Jak to dziaa?
Na pierwszy rzut oka wyglda to ma zwyke delegowanie wywoania do metody compare()
komparatora oryginalnego; jeli jednak spojrze uwanie po raz drugi, atwo mona zauway,
e delegowaniu temu towarzyszy zmiana kolejnoci argumentw: przykadowo wywoanie

Rozdzia 6. n Sortowanie proste algorytmy

159

compare("A", "B") w interfejsie odwrotnym (ReverseComparator) delegowane jest do interfejsu oryginalnego jako wywoanie compare("B", "A"). Zamiana kolejnoci argumentw

wywoania metody daje w konsekwencji odwrotny wynik samej metody.


Poniewa nie interesuj nas adne atrybuty porwnywanych obiektw, a jedynie wynik ich
porwnania, opisane rozwizanie jest w peni uniwersalne: implementacja komparatora
ReverseComparator jest cakowicie niezalena od implementacji komparatora oryginalnego.
Skoro opisalimy ju porwnywanie elementw i jego implikacje w postaci komparatorw,
zajmijmy si teraz trzema rnymi algorytmami sortowania.

Zanim przejdziemy do sortowania bbelkowego (bubblesort), musimy zdefiniowa kilka


przypadkw testowych dla rnych implementacji sortowania. Poniewa kady algorytm
sortowania testowany bdzie pod ktem spenienia tego samego kryterium poprawnego
porzdkowania sortowanych obiektw zwyczajowo rozpoczniemy od zdefiniowania
klasy bazowej definiujcej te aspekty testowania, ktre s wsplne dla wszystkich algorytmw. Specyfik konkretnych algorytmw powierzymy natomiast poszczeglnym klasom
pochodnym. W ten sposb otrzymamy zestaw testowy, ktry atwo bdzie mona przystosowywa do dowolnych algorytmw sortowania nawet takich, ktrych by moe jeszcze
dzi nie znamy.

sprbuj sam Przeprowadzanie sortowania bbelkowego


Wyobramy sobie rodzin udajc si do fotografa. Na wsplnej fotografii czonkowie rodziny powinni ustawi si wedug starszestwa, od najmodszego do najstarszego, gdy tymczasem ustawieni s w sposb przypadkowy, jak na rysunku 6.1.
Rysunek 6.1.
Rodzina
w przypadkowym
szyku

Aby dokona przestawienia czonkw rodziny wedug algorytmu sortowania bbelkowego,


porwnamy dwie skrajne osoby z lewej strony; nie s one ustawione zgodnie z wymaganiami pierwsza z nich jest starsza od drugiej poprosimy je wic, by zamieniy si
miejscami, co da efekt widoczny na rysunku 6.2.
Porwnujc osob drug i trzeci, stwierdzamy, e ich wzgldna kolejno jest prawidowa.
Nie mona tego powiedzie o osobie czwartej i pitej, ktre musz zamieni si miejscami,
doprowadzajc do konfiguracji przedstawionej na rysunku 6.3.

160

Algorytmy. Od podstaw

Rysunek 6.2.
Po pierwszej
zamianie miejsc

Rysunek 6.3.
Po wykonaniu
pierwszego kroku
najstarsza osoba
znajduje si ju
na swoim miejscu,
czyli na skrajnej
prawej pozycji

Cho senior rodu zajmuje ju waciw pozycj, kolejno, w jakiej ustawione s pozostae
osoby, nadal pozostawia wiele do yczenia, mimo e wykonalimy ju kilka porwna i przestawie. Na razie musimy si pogodzi z tak nieefektywnym sortowaniem, w nastpnym
rozdziale poznamy jego efektywniejsze algorytmy.
Kolejny krok sortowania bbelkowego przebiega identycznie jak pierwszy z t jednak rnic, e skrajna prawa pozycja jest ju waciwie obsadzona i moemy j pomin w porwnaniach. Ostatecznie krok ten doprowadza do tego, e druga co do starszestwa osoba
trafia na przeznaczon dla niej pozycj, jak na rysunku 6.4.
Rysunek 6.4.
Po wykonaniu
drugiego kroku
sortowania
dwie najstarsze
osoby stoj ju na
swoich miejscach

Wykonujc jeszcze dwa kroki sortowania, z udziaem najpierw trzech, a potem dwch osb,
otrzymamy ostatecznie podany ukad widoczny na rysunku 6.5.
Rysunek 6.5.
Rodzina prawidowo
ustawiona wedug
starszestwa

Rozdzia 6. n Sortowanie proste algorytmy

161

Interfejs ListSorter
Jak wiele interfejsw interfejs ListSorter jest skrajnie prosty, zawiera bowiem tylko jedn
metod, odpowiedzialn za posortowanie listy.
Metoda sort() otrzymuje list jako argument wejciowy i zwraca jako wynik jej posortowan wersj. Zalenie od implementacji lista wynikowa moe by list oryginaln, w ktrej
poprzestawiano elementy (sortowanie w miejscu) lub list nowo utworzon, zawierajc
kopie elementw pierwszej listy.
public interface ListSorter {
public List sort(List list);
}

Abstrakcyjna klasa testowa dla sortowania list


Mimo i nie napisalimy jeszcze ani jednej linijki algorytmu sortujcego, rozpoczniemy od
stworzenia zestawu testowego weryfikujcego poprawno dowolnej implementacji interfejsu ListSorter. Zgodnie z wczeniejszymi uwagami za podstaw konstrukcyjn wszystkich testw posuy nam abstrakcyjna klasa AbstractListSorterTest, obejmujca wszelkie
aspekty testowe niezalene od konkretnego algorytmu sortowania, w szczeglnoci:
n

utworzenie nieposortowanej listy acuchw,

utworzenie posortowanej listy tych samych acuchw sucej jako wzorzec


oczekiwanego rezultatu sortowania,

utworzenie instancji klasy implementujcej interfejs ListSorter ta operacja


wykonywana jest w ramach metody abstrakcyjnej wymagajcej zdefiniowania
w klasie pochodnej,

posortowanie oryginalnej listy za pomoc metody sort() instancji utworzonej


w poprzednim punkcie,

porwnanie wynikw sortowania z wzorcem utworzonym w punkcie drugim.

sprbuj sam Tworzenie abstrakcyjnej klasy testowej


Dwie pierwsze z wymienionych przed chwil czynnoci utworzenie listy wejciowej i jej
posortowanego odpowiednika dokonywane s przez metod settp() klasy testowej.
package com.wrox.algorithms.sorting;
import com.wrox.algorithms.lists.LinkedList;
import com.wrox.algorithms.lists.List;
import junit.framework.TestCase;
public abstract class AbstractListSorterTest extends TestCase {
private List _unsortedList;
private List _sortedList;
protected void settp() throws Exception {

162

Algorytmy. Od podstaw
_unsortedList = new LinkedList();
_unsortedList.add("programowanie");
_unsortedList.add("sterowane");
_unsortedList.add("testami");
_unsortedList.add("to");
_unsortedList.add("may");
_unsortedList.add("krok");
_unsortedList.add("dla");
_unsortedList.add("programisty");
_unsortedList.add("lecz");
_unsortedList.add("olbrzymi");
_unsortedList.add("skok");
_unsortedList.add("w");
_unsortedList.add("dziejach");
_unsortedList.add("programowania");
_sortedList = new LinkedList();
_sortedList.add("dla");
_sortedList.add("dziejach");
_sortedList.add("krok");
_sortedList.add("lecz");
_sortedList.add("may");
_sortedList.add("olbrzymi");
_sortedList.add("programisty");
_sortedList.add("programowania");
_sortedList.add("programowanie");
_sortedList.add("skok");
_sortedList.add("sterowane");
_sortedList.add("testami");
_sortedList.add("to");
_sortedList.add("w");
}

Obydwie listy zostaj zwolnione przez metod tearDown():


protected void tearDown() throws Exception {
_sortedList = null;
_unsortedList = null;
}

Musimy jeszcze zadeklarowa abstrakcyjn metod tworzc instancj implementujc interfejs ListSorter:
protected abstract ListSorter createListSorter(Comparator comparator);

i zdefiniowa metod wykonujc waciwy test:


public void testListSorterCanSortSampleList() {
ListSorter sorter = createListSorter(naturalComparator.INSTANCE);
List result = sorter.sort(_unsortedList);
assertEquals(result.size(), _sortedList,size());
Iterator actual = result.iterator();
actual.first();

Rozdzia 6. n Sortowanie proste algorytmy

163

Iterator expected = _sortedList.iterator();


expected.first();
while (!expected.isDone()) {
assertEquals(expected.current(), actual.current());

expected.next();
actual.next();

Jak to dziaa?
W pierwszym wierszu tworzona jest instancja klasy realizujcej okrelony algorytm sortowania; sortowanie odbywa si w naturalnej kolejnoci alfabetycznej acuchw specyfikowanym komparatorem jest bowiem komparator naturalny. W drugim wierszu wspomniany algorytm jest fizycznie realizowany w testowej licie _unsortedList. Po zakoczeniu
sortowania jego wynik porwnywany jest ze wzorcem: w stosunku do obydwu list wynikowej i wzorcowej najpierw porwnywane s ich rozmiary, a nastpnie przy uyciu
iteratorw porwnywane s kolejne pary odpowiadajcych sobie elementw. Identyczno
obydwu list jest warunkiem, ktry spenia musi dowolna implementacja algorytmu sortowania, jeeli w ogle zamierzamy jej uy do posortowania czegokolwiek!
Przechodzc od ogu do szczegw, zajmijmy si testowaniem sortowania bbelkowego.

sprbuj sam Testowanie klasy BubbleListSorter


Testow klas dla sortowania bbelkowego BubbleListSorterTest wyprowadzimy
z klasy abstrakcyjnej AbstractListSorterTest, implementujc odpowiednio jej metod
createListSorter().
package com.wrox.algorithms.sorting;
public class BubblesortListSorterTest extends AbstractListSorterTest {
protected ListSorter createListSorter(Comparator comparator) {
return new BubblesortListSorter(comparator);
}
}

Z kompilacj powyszego kodu musimy jednak poczeka, a zdefiniujemy klas BubblesortListSorter uczynimy to niebawem.

Jak to dziaa?
Klasa BubbleListSorterTest, mimo i jej zdefiniowanie sprowadzao si do zdefiniowania
jednej metody, dziedziczy po klasie bazowej AbstractListSorterTest zestaw danych testowych oraz metod testListSorterCanSortSampleList() zawierajc ca logik testow.
Konkretyzuje ona jedyny abstrakcyjny element tej logiki metod createListSorter()
tworzc instancj klasy reprezentujcej algorytm sortujcy.

164

Algorytmy. Od podstaw

sprbuj sam Implementowanie algorytmu sortowania bbelkowego


klasa BubbleListSorter
Implementacja klasy realizujcej algorytm sortowania bbelkowego musi spenia trzy nastpujce kryteria:
n

musi implementowa interfejs ListSorter,

musi dopuszcza dowolny komparator okrelajcy uporzdkowanie elementw,

musi przej pozytywnie testy opisane przed chwil.

Majc na uwadze powysze wymogi, rozpocznijmy od zdefiniowania konstruktora:


package com.wrox.algorithms.sorting;
import com.wrox.algorithms.lists.List;
public class BubblesortListSorter implements ListSorter {
private final Comparator _comparator;

/**
* Konstruktor
* parametr: komparator okrelaj cy uporz dkowanie elementrw
*/
public BubblesortListSorter(Comparator comparator) {
assert comparator != null : "nie okrelono komparatora";
_comparator = comparator;
}
...

Teraz przed nami najwaniejsze implementacja samego algorytmu sortowania bbelkowego. Jak pamitamy, algorytm ten wymaga wielu przej przez sortowan list; w wyniku
kadego przejcia kolejny element w pobliu koca listy ustawiany jest na swej waciwej
pozycji. Wynika std, e dla N-elementowej listy po wykonaniu N1 krokw na swych docelowych pozycjach znajdzie si N1 kocowych elementw, a wic take i element pocztkowy, ergo liczba krokw potrzebnych do posortowania dowolnej listy jest o jeden
mniejsza od liczby elementw zawartych w tej licie. Kod odpowiedzialny za powtarzanie
wspomnianych krokw nazwiemy ptl zewntrzn (outer loop).
W kadym kroku porwnywane s pary ssiadujcych elementw; jeeli wzgldna kolejno elementw pary nie jest zgodna z kryterium okrelonym przez komparator, elementy
zamieniane s miejscami ten cykl nazwiemy ptl wewntrzn (inner loop). Poniewa
w kadym kroku kolejny element kocowy lduje na swej pozycji docelowej, liczba elementw porwnywanych w kolejnych krokach systematycznie si zmniejsza: w pierwszym
kroku musimy wykona N1 porwna, w drugim N2 itd. Wyjania to warunek kontynuowania ptli wewntrznej left < (size - pass).
public List sort(List list) {
assert list != null : "nie okrelono listy wejciowej";
int size = list.size();
for (int pass = 1; pass < size; ++pass) {

// ptla zewntrzna

Rozdzia 6. n Sortowanie proste algorytmy

}
}

165

for (int left = 0; left < (size - pass); ++left) {


// ptla wewntrzna
int right = left + 1;
if (_comparator.compare(list.get(left), list.get(right)) > 0) {
swap(list, left, right);
}
}

return list;

Jak przed chwil wspomnielimy, jeli kolejno ssiadujcych elementw nie jest zgodna
z kryterium okrelonym przez komparator, elementy te zamieniane s miejscami. Musimy
wic dysponowa metod zamieniajc miejscami wartoci elementw o wskazanych indeksach.
private void swap(List list, int left, int right) {
Object temp = list.get(left);
list.set(left, list.get(right));
list.set(right, temp);
}

Po zaimplementowaniu i (pomylnym) przetestowaniu klasy BubblesortListSorter mona


celowo sprowokowa zaamanie testu, na przykad zmieniajc wzorcow list w taki sposb, by nie speniaa kryterium sortowania. Prdzej czy pniej trzeba jednak zaj si kolejnym algorytmem sortowania.

Wyobra sobie ksiki o rnej wysokoci, przypadkowo uoono na pce, jak przedstawia
to rysunek 6.6. Wanie spodziewasz si odwiedzin mamy i chcesz jej zaimponowa swoich zamiowaniem do porzdku domowego, postanawiasz wic poukada ksiki wedug
malejcej wysokoci od lewej do prawej.
Rysunek 6.6.
Pka z losowo
ustawionymi
ksikami

166

Algorytmy. Od podstaw
Sortowanie bbelkowe raczej si do tego nie nada, bo przestawianie ssiednich par byoby
strat czasu zamiana miejscami dwch ksiek trwa bowiem znacznie duej ni porwnanie ich wysokoci. Zdecydowanie lepsz metod na uzyskanie danego uoenia ksiek
bdzie sortowanie przez wybieranie, zwane take sortowaniem przez selekcj (selectionsort).
Znajd na pce najwysz ksik i zdejmij j z pki. Powiniene j ustawi jako pierwsz od lewej; zamiast przesuwa w prawo by moe du liczb innych ksiek, po prostu
zamie j z t, ktra aktualnie znajduje si najbardziej na lewo (nie unikniesz cakowicie
przesuwania ksiek, bowiem zapewne rni si one od siebie gruboci, ten szczeg nie
ma jednak znaczenia w sytuacji, gdy zamiast ksiek sortowane s elementy listy). Opisana
zamiana ksiek, zamiast przesuwania caej ich grupy, pozbawia sortowanie pewnej wasnoci zwanej stabilnoci; zajmiemy si ni w rozdziale 7., na razie jest ona bez znaczenia. Ukad ksiek po pierwszej zamianie przedstawiony jest na rysunku 6.7.

Rysunek 6.7.
Najwysza ksika
znajduje si
ju na skrajnej
lewej pozycji

Jak atwo si domyli, w kolejnym kroku naley odszuka najwysz z pozostaych ksiek i zamieni j miejscami z t, ktra aktualnie zajmuje pozycj drug od lewej. Efekt tej
zamiany przedstawiony jest na rysunku 6.8.
Rysunek 6.8.
Druga co do
wysokoci ksika
znajduje si na
waciwej pozycji

Rozdzia 6. n Sortowanie proste algorytmy

167

Kontynuujc konsekwentnie to postpowanie, wybieramy z nieposortowanej jeszcze grupy


ksiek najwysz i wstawiamy j na kolejne miejsce od lewej dlatego wanie opisana
metoda nazywa si sortowaniem przez wybieranie. Kolejne stadia sortowania z uyciem tej
metody przedstawione s na rysunku 6.9.
Rysunek 6.9.
Kolejne pozycje
od lewej strony
zapeniane
s waciwymi
ksikami

168

Algorytmy. Od podstaw
Oczywicie moe si tak zdarzy, e w ktrym stadium sortowania ksika bdzie ju
znajdowa si na swej pozycji docelowej i adne przestawianie nie bdzie wwczas konieczne. Tak czy inaczej nie zmienia to podstawowej wasnoci sortowania przez wybr
tej mianowicie, e grupa elementw jeszcze nieposortowanych, pocztkowo obejmujca
wszystkie elementy, zmniejsza si systematyczne, rozrasta si natomiast grupa elementw
ju posortowanych, pocztkowo pusta, a w kocu obejmujca wszystkie elementy. Co wicej, wybierana ksika od razu trafia na sw docelow pozycj, w przeciwiestwie do sortowania bbelkowego, gdzie elementy stopniowo przesuwane s maymi krokami.
Znaczna cz kodu testowego stworzonego przy okazji sortowania bbelkowego moe by
wykorzystana przy okazji sortowania przez wybieranie. Rozpoczniemy od stworzenia zestawu testowego, po czym zajmiemy si samym algorytmem sortowania.

sprbuj sam Testowanie klasy SelectionSortListSorter


Klas testujc sortowanie przez wybieranie SelectionSortListSorterTest skonstruujemy w taki sam sposb jak klas testow dla sortowania bbelkowego zaimplementujemy odpowiednio metod abstrakcyjn createListSorter() tak, by zwracaa instancj klasy
SelectionSortListSorter.
package com.wrox.algorithms.sorting;
/**
*/
public class SelectionSortListSorterTest extends AbstractListSorterTest {
protected ListSorter createListSorter(Comparator comparator) {
return new SelectionSortListSorter(comparator);
}
}

Jak to dziaa?
Klasa testowa SelectionSortListSorterTest dziedziczy po swej klasie bazowej AbstractListSorterTest wszystkie dane testowe i ca logik testow. Jedynym elementem specyficznym dla sortowania przez wybieranie jest zaimplementowana metoda createListSorter(), dostarczajca instancji klasy realizujcej algorytm sortowania.

sprbuj sam Implementowanie klasy SelectionSortListSorter


Klasa SelectionSortListSorter jest pod wieloma wzgldami podobna do klasy BubbleSortListSorter: implementuje interfejs ListSorter, dziaa w oparciu o komparator wyznaczajcy
kryterium sortowania i oczywicie musi pomylnie zaliczy testy przeprowadzane w oparciu
o odpowiedni klas testow. Rozpoczniemy od konstruktora klasy:
public class SelectionSortListSorter implements ListSorter {
private final Comparator _comparator;
/**
* Konstruktor
* parametr: komparator okrelaj cy uporz dkowanie elementrw
*/

Rozdzia 6. n Sortowanie proste algorytmy

169

public SelectionSortListSorter(Comparator comparator) {


assert comparator != null : "nie okrelono komparatora";
_comparator = comparator;
}
...
}

Jak to dziaa?
Implementacja sortowania przez wybieranie ma posta dwch zagniedonych ptli zewntrznej i wewntrznej podobnie jak w przypadku sortowania bbelkowego. Jest jednak kilka istotnych rnic, nie od razu zauwaalnych. Po pierwsze, ptla zewntrzna przebiega indeksy od 0 do N2, a nie od 1 do N1. Liczba krokw pozostaje ta sama, lecz
zmienna sterujca ptli rwna jest pozycji docelowej, na ktrej umieszczany jest kolejny
element w pierwszym kroku jest to pozycja 0, w drugim pozycja 1 itd. Po wykonaniu
N1 krokw ostatni, N-ty element samoczynnie znajduje si ju na waciwej pozycji.
Po drugie, w ptli wewntrznej nie dokonuje si adnych przestawie, a jedynie wyszukuje
(w grupie nieposortowanych jeszcze elementw) element o najmniejszej wartoci. Co prawda
jest to sytuacja odwrotna do przykadu z ksikami, gdzie sortowanie nastpowao wedug
malejcej wysokoci, lecz dla algorytmu jako takiego nie ma to wikszego znaczenia
w razie potrzeby zawsze mona uy komparatora odwrotnego.
public List sort(List list) {
assert list != null : "nie okrelono listy";
int size = list.size();
for (int slot = 0; slot < size - 1; ++slot) {
int smallest = slot;
for (int check = slot + 1; check < size; ++check) {
if (_comparator.compare(list.get(check), list.get(smallest)) < 0) {
smallest = check;
}
}
swap(list, smallest, slot);
}
return list;
}

Po trzecie, istnieje pewna drobna, lecz istotna rnica w procedurze przestawiajcej elementy. Moe si ot zdarzy, e kolejny element bdzie si ju znajdowa na swoim miejscu i przestawianie go (z samym sob) bdzie niepotrzebne (w sortowaniu bbelkowym
sytuacja taka nie moga si zdarzy, bowiem przestawianie dotyczyo zawsze ssiadujcych
elementw). Metoda swap() sprawdza wic kadorazowo, czy elementy specyfikowane do
przestawienia s istotnie rne:
private void swap(List list, int left, int right) {
if (left == right) {
// czy istotnie chodzi o rrne elementy
return;
// nie, nic nie rrb.
}

170

Algorytmy. Od podstaw
Object temp = list.get(left);
list.set(left, list.get(right));
list.set(right, temp);
}

Sortowanie przez wstawianie (insertionsort) charakterystyczne jest dla ukadania trzymanych w rku kart w kolejnoci wzrastajcej wanoci. Zamy, e ley przed Tob pi
odwrconych kart (rys. 6.10), ktre chciaby posortowa wedug nastpujcego kryterium:
n

najpierw piki (

), potem trefle (

), potem kara (

), a na kocu kiery (

w ramach danego koloru as (A), 2, 3, , 10, walet (J), dama (Q) i krl (K).

),

Rysunek 6.10.
Rka karciana
pi nieznanych
jeszcze kart

Odkrywamy pierwsz kart; nie ma nic prostszego jak posortowanie jednego elementu,
wic po prostu odkadamy kart do grupy elementw posortowanych. W sytuacji na rysunku
6.11 odkryt kart jest sidemka karo.
Rysunek 6.11.
Pojedyncza karta
jest zawsze
posortowana

Niech druga odkryta karta bdzie waletem pik (rysunek 6.12). Wedug przyjtego kryterium poprzedza ona sidemk karo, wstawiamy j wic na pierwsz pozycj.
Trzecia karta okazuje si by asem trefl i wedug przyjtej kolejnoci plasuje si midzy
dwiema ju odkrytymi (rysunek 6.13).

Rozdzia 6. n Sortowanie proste algorytmy

171

Rysunek 6.12.
Druga karta
zostaje wstawiona
przed pierwsz

Rysunek 6.13.
Trzecia karta
zostaje wstawiona
midzy dwie
pozostae

Jak wic widzimy, sortowanie przez wstawianie polega na podziale sortowanych elementw na dwie grupy: posortowan (pocztkowo pust) i nieposortowan (obejmujc pocztkowo wszystkie elementy). W kadym z kolejnych krokw z grupy nieposortowanej
brany jest kolejny element i wstawiany na odpowiednie miejsce do grupy posortowanej
tak by pozostaa ona nadal posortowana. W ten sposb grupa nieposortowana stopniowo si
zmniejsza, a grupa posortowana powiksza si, by w kocu obj wszystkie elementy
jak na rysunku 6.14, po odkryciu wszystkich piciu kart.
Rysunek 6.14.
Odkrycie
przedostatniej
i ostatniej karty

sprbuj sam Testowanie klasy InsertionSortListSorter


Podobnie jak w przypadku dwch poprzednich algorytmw sortowania klas testow wyprowadzimy z abstrakcyjnej klasy AbstractListSorterTest, konkretyzujc jej metod createListSorter().

172

Algorytmy. Od podstaw
package com.wrox.algorithms.sorting;
public class InsertionSortListSorterTest extends AbstractListSorterTest {
protected ListSorter createListSorter(Comparator comparator) {
return new InsertionSortListSorter(comparator);
}
}

Jak to dziaa?
Tak jak poprzednio klasa testowa (InsertionSortListSorterTest) dziedziczy po swej klasie bazowej AbstractListSorterTest wszystkie dane testowe i ca logik testow. Jedynym elementem specyficznym dla sortowania przez wstawianie jest zaimplementowana metoda createListSorter(), dostarczajca instancji klasy realizujcej algorytm sortowania.

sprbuj sam Implementowanie klasy InsertionSortListSorter


Podobnie jak dwie poprzednie klasy implementujce algorytmy sortowania klasa InsertionSortListSorter implementuje interfejs ListSorter, jej dziaanie opiera si na porzdku
wyznaczanym przez komparator i moe by weryfikowane za pomoc odpowiedniej klasy
testowej.
package com.wrox.algorithms.sorting;
import com.wrox.algorithms.lists.List;
import com.wrox.algorithms.lists.LinkedList;
import com.wrox.algorithms.iteration.Iterator;
public class InsertionSortListSorter implements ListSorter {
private final Comparator _comparator;
/**
* Konstruktor
* parametr: komparator okrelaj cy uporz dkowanie elementrw
*/

public InsertionSortListSorter(Comparator comparator) {


assert comparator != null : "nie okrelono komparatora";
_comparator = comparator;
}
...

Metoda sort() klasy InsertionSortListSorter rni si zasadniczo od tej implementowanej w klasach BubbleSortListSorter i SelectionSortListSorter pod jednym wzgldem:
zamiast sortowania zawartoci listy w miejscu tworzymy now, pust list wynikow i sukcesywnie wstawiamy do niej (na waciw pozycj) elementy pobierane kolejno z listy wejciowej.
public List sort(List list) {
assert list != null : "nie okrelono listy wejciowej";
final List result = new LinkedList();

Rozdzia 6. n Sortowanie proste algorytmy

173

Iterator it = list.iterator();
for (it.first(); !it.isDone(); it.next()) {
// ptla zewntrzna
int slot = result.size();
while (slot > 0) {
// ptla wewntrzna
if (_comparator.compare(it.current(), result.get(slot - 1)) >= 0) {
break;
}
--slot;
}
result.insert(slot, it.current());
}
return result;
}

Jak to dziaa?
W zewntrznej ptli for za pomoc iteratora pobierane s kolejne elementy listy wejciowej; uycie iteratora jest rozwizaniem bardziej uniwersalnym ni bezporedni dostp do
elementw na podstawie ich indeksw. W ptli wewntrznej ktra nie jest ptl for, lecz
ptl while w (stopniowo zapenianej) licie wynikowej poszukiwana jest pozycja, na
ktr naley wstawi element pobrany z listy wejciowej. W przeciwiestwie do listy wejciowej, ktrej implementacja jest bez znaczenia, lista wynikowa jest list wizan LinkedList,
a dostp do jej elementw odbywa si w sposb bezporedni. Wybralimy list wizan ze
wzgldu na efektywno, z jak mona wstawia do niej elementy. Lista wynikowa pozostaje
cay czas posortowana, a po wstawieniu do niej ostatniego elementu sortowanie si koczy.
Zwrmy ponadto uwag, e poszukiwanie (w ptli wewntrznej) waciwej pozycji w licie wynikowej rozpoczyna si od jej koca. Mimo i nie wpywa to na wydajno sortowania przecitnej listy, to jednak drastycznie poprawia t wydajno w przypadku, gdy lista
wejciowa jest ju posortowana (lub prawie posortowana) wstawienie elementu (a waciwie jego doczenie) odbywa si ju po wykonaniu jednego porwnania. Powrcimy do
tej kwestii przy okazji porwnywania prostych algorytmw sortowania w dalszej czci niniejszego rozdziau. Kierunek przegldania posortowanej listy wynikowej nie jest natomiast obojtny z punktu widzenia stabilnoci sortowania.

Niektre algorytmy sortowania cechuj si interesujc wasnoci zwan stabilnoci. Aby


zrozumie jej istot, rozpatrzmy list pracownikw posortowan wedug imion (tabela 6.1).
Zamy teraz, e chcemy posortowa powysz list wedug nazwisk. Poniewa niektre
nazwiska si powtarzaj (Smith i Barnes), mona to zrobi na kilka sposobw i ostateczna
kolejno moe by rna dla rnych algorytmw sortowania. Poniewa pozycje o jednakowych nazwiskach wystpowa mog w dowolnej kolejnoci wzgldem siebie, wic w ramach tego samego nazwiska posortowanie wedug imion moe zosta zachowane lub nie.
Innymi sowy, algorytm sortowania moe, lecz nie musi zachowywa istniejc wzgldn
kolejno pozycji osb o tym samym nazwisku. Te algorytmy, ktre kolejno t zachowuj,
nazywamy algorytmami stabilnymi. Efekt posortowania listy z tabeli 6.1 w sposb stabilny
przedstawiony jest w tabeli 6.2.

174

Algorytmy. Od podstaw

Tabela 6.1. Lista posortowana wedug imion


Imi

Nazwisko

Albert

Smith

Brian

Jackson

David

Barnes

John

Smith

John

Wilson

Mary

Smith

Tom

Barnes

Vince

De Marco

Walter

Clarke

Tabela 6.2. Lista z tabeli 6.1 stabilnie posortowana wedug nazwisk


Imi

Nazwisko

David

Barnes

Tom

Barnes

Walter

Clarke

Vince

De Marco

Brian

Jackson

Albert

Smith

John

Smith

Mary

Smith

John

Wilson

Przykad niestabilnego posortowania wspomnianej listy wedug nazwisk przedstawiony jest


w tabeli 6.3 w ramach nazwiska Smith nie zostaa zachowana oryginalna kolejno
imion.
Tabela 6.3. Lista z tabeli 6.1 posortowana wedug nazwisk w sposb niestabilny
Imi

Nazwisko

David

Barnes

Tom

Barnes

Walter

Clarke

Vince

De Marco

Brian

Jackson

Albert

Smith

Mary

Smith

John

Smith

John

Wilson

Rozdzia 6. n Sortowanie proste algorytmy

175

Spord trzech opisanych dotd algorytmw sortowania algorytmem stabilnym jest sortowanie bbelkowe. To, czy stabilne jest sortowanie przez wstawianie, zalene jest od kolejnoci pobierania elementw z listy wejciowej i sposobu ich wstawiania do listy wynikowej;
prezentowana przez nas implementacja jest implementacj stabiln. Podobnie stabilno
sortowania przez wybieranie zaley od szczegw jego implementacji. Omawiane w nastpnym rozdziale bardziej zaawansowane algorytmy sortowania, cho cechuj si znaczco lepsz efektywnoci, nie s algorytmami stabilnymi i jest to jedna z ich wad w porwnaniu z prostymi algorytmami sortowania, o czym trzeba pamita przy tworzeniu aplikacji
o konkretnych wymaganiach.

Po zapoznaniu si z trzema prostymi algorytmami sortowania bbelkowego, przez wybieranie i przez wstawianie nie sposb nie zastanawia si, ktry z nich okae si najlepszy w danym zastosowaniu, a dokadniej jakimi kryteriami naley si kierowa przy
dokonywaniu jego wyboru. W niniejszym podrozdziale dokonamy porwnania wymienionych algorytmw; nie bdzie to formalne porwnanie matematyczne, lecz porwnanie
praktyczne oparte na obserwacji sortowania rzeczywistych danych. Nie jest naszym zadaniem definitywne sformuowanie kryteriw wyboru konkretnego algorytmu, lecz raczej pokazanie, jak wspomniana analiza porwnawcza moe dokonanie takiego wyboru uatwi.
Na pocztku tego rozdziau informowalimy, ze istot kadego sortowania jest intensywne
wykonywanie dwch operacji: porwnywania elementw i ich przestawiania. Nasza analiza porwnawcza koncentrowa si bdzie na pierwszej z tych operacji, a uywane na jej
potrzeby zestawy danych bd znacznie wiksze ni w zestawach testowych weryfikujcych poprawno implementacji algorytmw; jest to konieczne z tego wzgldu, e prawdziwy charakter kadego algorytmu, odzwierciedlany gwnie przez jego zachowanie asymptotyczne wyraone w notacji duego O, uwidacznia si dopiero przy rozwizywaniu problemw
o duych rozmiarach. Ponadto, poniewa konkretne dane wejciowe algorytmu maj zwykle wpyw na jego efektywno, analiz nasz przeprowadzimy w oparciu o trzy szczeglne
rodzaje danych wejciowych:
n

list ju posortowan,

list posortowan w kolejnoci odwrotnej do danej,

list o przypadkowej kolejnoci elementw.

Obserwujc zachowanie si czyli zliczajc wykonywane porwnania wszystkich


trzech algorytmw dla kadego z wymienionych przypadkw, bdzie mona w przyblieniu oceni, ktry algorytm nadaje si najlepiej dla danego przypadku napotkanego w rzeczywistej aplikacji.

176

Algorytmy. Od podstaw

CallCountingListComparator
Poniewa za wszystkie porwnania, jakie wykonywane s w ramach algorytmu sortowania,
odpowiedzialny jest komparator, a dokadniej jego metoda compare(), najprostszym sposobem zliczania porwna wydaje si przechwycenie wywoania tej metody, czyli wzbogacenie jej o fragment kodu dokonujcy zliczania wszystkich wywoa. Mona by te posun
si jeszcze dalej i wyposay w taki mechanizm zliczania w jak klas bazow, z ktrej
wyprowadzane byby wszystkie zliczajce komparatory. Wymagaoby to jednak ponownego zaimplementowania od podstaw tych komparatorw, ktre chcemy uczyni zliczajcymi. Chcc wykorzysta w jak najwikszym stopniu istniejcy kod, postpimy wic inaczej i funkcj zliczajc komparatora zrealizujemy za pomoc jego otoczki (dekoratora),
podobnie jak czynilimy to w przypadku odwracania uporzdkowania za pomoc klasy
ReverseComparator.
public final class CallCountingComparator implements Comparator {
/** komparator oryginalny, ktrry wyposaamy w funkcj zliczania */
private final Comparator _comparator;
/** zmienna przechowuj ca liczb zarejestrowanych wywoa
private int _callCount;

komparatora */

/**
* Konstruktor.
* Parametr: oryginalny komparator
*/
public CallCountingComparator(Comparator comparator) {
assert comparator != null : "nie okrelono komparatora";

_comparator = comparator;
_callCount = 0;

public int compare(Object left, Object right) throws ClassCastException {


++_callCount;
return _comparator.compare(left, right);
}
public int getCallCount() {
return _callCount;
}
}

Podobnie jak komparator odwrotny ReverseComparator, tak i komparator zliczajcy CallCountingComparator definiowany jest na bazie dowolnego komparatora przekazywanego jako
parametr wywoania konstruktora. Wywoanie metody compare() komparatora zliczajcego
jest rejestrowane poprzez zwikszenie wartoci zmiennej _callCount, po czym delegowane
jest do metody compare() komparatora oryginalnego. Warto zmiennej _callCount, rwna
liczbie dokonanych wywoa, dostpna jest za porednictwem metody getCallCount().
Majc do dyspozycji komparator zliczajcy, moemy tworzy zestawy testowe badajce
zachowanie si poszczeglnych algorytmw sortowania w odniesieniu do danych o rnym
charakterze.

Rozdzia 6. n Sortowanie proste algorytmy

177

ListSorterCallCountingTest
Mimo i tym razem nie zamierzamy testowa poprawnoci zachowania si kodu, lecz mierzy liczb porwna wykonywanych przez algorytmy sortowania, skorzystamy z biblioteki
Jtnit, bowiem podobnie jak w przypadku testw moduw bdziemy musieli wykona kilka dyskretnych scenariuszy dla kadego algorytmu poprzedzonych pewnymi czynnociami
przygotowawczymi (setup). Zdefiniujemy wic klas testow, a w ramach niej sta okrelajc rozmiar sortowanej listy, trzy listy o charakterystykach wczeniej wymienionych
(posortowan, posortowan odwrotnie i nieposortowan) oraz instancj komparatora zliczajcego.
package com.wrox.algorithms.sorting;
import com.wrox.algorithms.lists.ArrayList;
import com.wrox.algorithms.lists.List;
import junit.framework.TestCase;
public class ListSorterCallCountingTest extends TestCase {
private static final int TEST_SIZE = 1000;
// lista posortowana
private final List _sortedArrayList = new ArrayList(TEST_SIZE);
// lista posortowana odwrotnie
private final List _reverseArrayList = new ArrayList(TEST_SIZE);
// lista o przypadkowej kolejnoci elementrw
private final List _randomArrayList = new ArrayList(TEST_SIZE);
private CallCountingComparator _comparator;
...
}

Samo zdefiniowanie list _sortedArrayList, _reverseArrayList i _randomArrayList to dopiero


pocztek, musimy bowiem wypeni te listy wartociami w sposb odpowiadajcy ich zaoonej charakterystyce. Zakadamy, e elementami tymi bd liczby cakowite, czyli obiekty
typu integer. W przypadku dwch pierwszych list bd to kolejne liczby naturalne od 1 do
1 000 uszeregowane w kolejnoci (odpowiednio) rosncej i malejcej; w przypadku trzeciej
listy bd to losowe liczby cakowite z tego zakresu. Musimy take zdefiniowa komparator zliczajcy, ktry oprzemy na komparatorze naturalnym (NaturalComparator). Jest to dopuszczalne, bowiem typ java.lang.integer implementuje interfejs Comparable, podobnie
jak implementuj go acuchy wykorzystywane we wczeniejszych przykadach.
protected void settp() throws Exception {
super.settp();
_comparator = new CallCountingComparator(NaturalComparator.INSTANCE);
for (int i = 1; i < TEST_SIZE; ++i) {
_sortedArrayList.add(new Integer(i));
}
for (int i = TEST_SIZE; i > 0; --i) {
_reverseArrayList.add(new Integer(i));

178

Algorytmy. Od podstaw
}
for (int i = 1; i < TEST_SIZE; ++i) {
_randomArrayList.add(new Integer((int)(TEST_SIZE * Math.random())));
}
}

By zaobserwowa dziaanie kadego algorytmw dla listy posortowanej odwrotnie, naley


utworzy kolejno trzy odpowiednie implementacje interfejsu ListSorter i uy kadej
z nich do posortowania listy _reverseArrayList utworzonej w ramach metody settp().
Wnikliwy Czytelnik mgby w tym miejscu stwierdzi, e po pierwszym posortowaniu listy
_reverseArrayList dalsze sortowania nie maj sensu, bo lista ta przestanie by lista posortowan odwrotnie. Ot jest zupenie inaczej: lista _reverseArrayList tworzona jest na
nowo przed kadym z sortowa przed wywoaniem kadej z metod testowych wywoywana jest metoda settp() i to jest gwny powd, dla ktrego uylimy biblioteki Jtnit w zastosowaniu niemajcym nic wsplnego z weryfikacj poprawnoci kodu. Dziki temu wszystkie trzy metody testowe dziaaj niezalenie od siebie.
public void testReverseCaseBubblesort () {
new BubblesortListSorter(_comparator).sort(_reverseArrayList);
reportCalls();
}
public void testReverseCaseSelectionSort () {
new SelectionSortListSorter(_comparator).sort(_reverseArrayList);
reportCalls();
}
public void testReverseCaseInsertionSort () {
new InsertionSortListSorter(_comparator).sort(_reverseArrayList);
reportCalls();
}

Wyniki obserwacji kadego z sortowa, czyli informacja o liczbie wywoa metody compare() odnonego komparatora, wywietlane s za pomoc metody reportCalls(), ktr opiszemy za chwil. W podobny sposb przeprowadzimy obserwacj dla listy posortowanej
w danej kolejnoci
public void testDirectCaseBubblesort () {
new BubblesortListSorter(_comparator).sort(_sortedArrayList);
reportCalls();
}
public void testDirectCaseSelectionSort () {
new SelectionSortListSorter(_comparator).sort(_sortedArrayList);
reportCalls();
}
public void testDirectCaseInsertionSort () {
new InsertionSortListSorter(_comparator).sort(_sortedArrayList);
reportCalls();
}

i dla listy o losowym ukadzie elementw:

Rozdzia 6. n Sortowanie proste algorytmy

179

public void testRandomCaseBubblesort () {


new BubblesortListSorter(_comparator).sort(_randomArrayList);
reportCalls();
}
public void testRandomCaseSelectionSort () {
new SelectionSortListSorter(_comparator).sort(_randomArrayList);
reportCalls();
}
public void testRandomCaseInsertionSort () {
new InsertionSortListSorter(_comparator).sort(_randomArrayList);
reportCalls();
}

Wspomniana wczeniej metoda reportCalls() odczytuje za pomoc metody callCount() licznik dokonanych porwna i wyprowadza jego warto poprzedzon nazw klasy
testowej:
private void reportCalls() {
System.out.println(getName() + ": " + _comparator.getCallCount() + " wywoa ");
}

Nazwa klasy testowej jak atwo si zorientowa udostpniana jest przez metod getName(), ktra jest metod klasy bazowej TestCase biblioteki Jtnit. Oto przykadowy raport
dla listy posortowanej odwrotnie:
testReverseCaseBubblesort: 499500 wywoa
testReverseCaseSelectionSort: 499500 wywoa
testReverseCaseInsertionSort: 499500 wywoa

Jak wida, wszystkie trzy algorytmy sortowania wykonay tak sam liczb porwna dla
listy wyglda na to, e jest ona jednakowo trudnym przypadkiem dla kadego z nich.
Nie naley jednak przyjmowa tego jako reguy, a w przypadku danych o charakterze wycznie empirycznym (jak tutaj) naley wystrzega si formuowania pochopnych, by moe z gruntu faszywych wnioskw, cho oczywicie nie mona nie zastanawia si nad
przyczynami obserwowanych faktw.
Wyniki analogicznej analizy dla listy ju posortowanej wygldaj zgoa odmiennie:
testDirectCaseBubblesort: 49t501 wywoa
testDirectCaseSelectionSort: 49t501 wywoa
testDirectCaseInsertionSort: 99t wywoa

Tak dua wraliwo sortowania przez wstawianie na fakt posortowania listy wejciowej
nie powinna by zaskoczeniem. Jej przyczyn wyjanialimy wczeniej jest ni szczeglny
sposb przeszukiwania listy wynikowej, poczwszy od jej koca, nie pocztku.
Na koniec pozostaje porwnanie zachowania si algorytmw sortowania dla typowej, nieuporzdkowanej listy:
testRandomCaseBubblesort: 49t501 wywoa
testRandomCaseSelectionSort: 49t501 wywoa
testRandomCaseInsertionSort: 262095 wywoa

180

Algorytmy. Od podstaw
Algorytm sortowania przez wstawianie wykonuje, jak wida, dwukrotnie mniej porwna
ni kady z dwch pozostaych algorytmw.

Jak interpretowa wyniki tej analizy?


Z przeprowadzonej analizy porwnawczej powinnimy oczywicie wycign pewne wnioski, musimy jednak by przy tym wiadomi warunkw, w jakich analiza ta zostaa przeprowadzona. Aby mianowicie pozna prawdziwe oblicze kadego z algorytmw, naleaoby
wzbogaci t analiz o co najmniej nastpujce elementy:
n

zliczanie take operacji przestawiania elementw, a nie tylko operacji ich


porwnywania,

wykorzystanie rnych implementacji list, na przykad tablicowej, a nie tylko wizanej,

pomiar rzeczywistego czasu wykonania kadego z sortowa.

Mimo wspomnianych ogranicze moemy jednak pokusi si o nastpujce ustalenia:


n

Algorytmy sortowania bbelkowego i sortowania przez wybieranie wykonuj


zawsze t sam liczb porwna dla tych samych danych wejciowych.

Liczba operacji wykonywanych zarwno w sortowaniu bbelkowym, jak i w sortowaniu


przez wybieranie, jest niezalena od charakteru sortowanych danych.

Liczba operacji wykonywanych w sortowaniu przez wstawianie jest w duym


stopniu zalena od charakteru sortowanych danych. W najgorszym przypadku
liczba ta jest rwna liczbie porwna wykonywanych przez dwa pozostae
algorytmy (dla tych samych danych), w najlepszym przypadku jest ona mniejsza
od liczby sortowanych elementw.

By moe najwaniejszym wnioskiem podsumowujcym analiz jest niewraliwo sortowania bbelkowego i sortowania przez wybieranie na charakter sortowanych danych. W przeciwiestwie do nich sortowanie przez wstawianie wykazuje due zdolnoci adaptacyjne: jeli
mona posortowa dane mniejszym nakadem pracy, algorytm istotnie wykorzystuje t moliwo. Jest to gwn przyczyn faworyzowania w praktyce sortowania przez wstawianie
w stosunku do sortowania bbelkowego i sortowania przez wybieranie.

W niniejszym rozdziale:
n

zaimplementowalimy trzy proste algorytmy sortowania bbelkowe,


przez wybieranie i przez wstawianie i zweryfikowalimy poprawno ich
implementacji za pomoc odpowiednich zestaww testowych,

opisalimy koncepcj komparatora i zaimplementowalimy kilka komparatorw


komparator naturalny, komparator odwrotny i komparator zliczajcy,

Rozdzia 6. n Sortowanie proste algorytmy


n

porwnalimy liczb porwna wykonywanych przez kady z trzech wymienionych


algorytmw sortowania dla trzech szczeglnych rodzajw danych wejciowych:
listy ju posortowanej, listy posortowanej odwrotnie i listy o losowym ukadzie
elementw oraz sformuowalimy oglne wnioski na temat charakteru kadego
z algorytmw,

wyjanilimy pojcie stabilnoci algorytmu sortowania.

181

Lektura niniejszego rozdziau z pewnoci pozwoli Czytelnikom lepiej zrozumie znaczenie sortowania dla innych czynnoci algorytmicznych, na przykad wyszukiwania. Tre
rozdziau jest ponadto dowodem na to, e rozmaite problemy algorytmiczne mog by rozwizywane na rne sposoby w szczeglnoci istnieje kilka rnych metod porzdkowania elementw w zadanej kolejnoci. W nastpnym rozdziale poznamy inne, bardziej
zoone metody sortowania, ktre dla bardzo duych rozmiarw danych wejciowych okazuj si znacznie efektywniejsze od metod dotychczas opisanych.

1. Stwrz zestawy testowe weryfikujce poprawno sortowania przez kady

z algorytmw losowo wygenerowanej listy obiektw typu double.


2. Stwrz zestawy testowe udowadniajce, e sortowanie bbelkowe i sortowanie

przez wstawianie (w implementacjach prezentowanych w niniejszym rozdziale)


s stabilnymi metodami sortowania.
3. Skonstruuj komparator wyznaczajcy alfabetyczn kolejno acuchw,

bez rozrniania maych i wielkich liter.


4. Napisz program-sterownik zliczajcy liczb przestawie obiektw w ramach

kadego z opisywanych w rozdziale algorytmw sortowania.

You might also like