Krótki wstęp.
Sortowanie jest podstawowym, ale bardzo ważnym zagadnieniem w informatyce. Sortowaniu podlegają nie tylko tablice, ale często ogromne ilości danych w bazach danych, czy struktury danych dynamiczne, takie jak kolejki czy listy wiązane. Dlatego bardzo ważnym problemem jest jak sortować najefektywniej, aby zużyć jak najmniej dodatkowej pamięci, a przede wszystkim czasu.
Istnieje bardzo wiele bardzo różnych algorytmów, które realizują sortowanie. Wśród nich wymienia się takie, które działają w miejscu, czyli nie potrzebują dodatkowej pamięci (zawsze, co najwyżej kilka elementów ciągu, który mamy posortować wymaga "wyjęcia" z tablicy), a także takie, dzięki którym szybko sortuje się elementy, gdzie często powtarza się pewien klucz sortowania.
Jednak największym problemem, na jaki natrafia się przy projektowaniu algorytmów sortowania jest złożoność czasowa. Algorytmy sortujące, które wykorzystują w swoim działaniu porównania, nie mogą (zostało to udowodnione) działać szybciej niż ze złożonością czasową O(nlgn). Na szczęście w niektórych zastosowaniach można używać algorytmów zliczających, które nie wykonują porównań. Dzięki temu można sortować niektóre ciągi ze złożonością liniową, czyli O(n). Przykładami algorytmów, które ni wykonują porównań przy sortowaniu, są sortowania kubełkowe, przez zliczanie i pozycyjne.
Kiedy nie zależy nam na liniowej złożoności to najlepiej sprawdzającym się w praktyce algorytmem sortowania jest algorytm QuickSort. Jest on najszybszy wśród algorytmów używających porównań, poza tym dzięki swojej strukturze rekurencyjnej jest bardzo zwięzły i prosty w napisaniu.
Rozpoczniemy od algorytmów wykorzystujących porównania.
Sortowanie bąbelkowe, bubble sort
Sortowanie bąbelkowe jest jednym z najprostszych algorytmów sortowania. Polega na przejrzeniu elementów do posortowania i zamienianiu miejscami elementów, które znajdują się obok siebie. Przypomina to trochę klasyfikację niektórych elementów jako "lżejsze", które wypływają na powierzchnię, podczas gdy inne, "cięższe" pozostają niżej. Złożoność czasowa algorytmu sortowania bąbelkowego wynosi O(n2). Bardzo dużą zaletą sortowania bąbelkowego jest to, że jest stabilne, czyli w każdym momencie jego działania elementy już ustawione na swoich miejscach tam pozostają. Przy sortowaniu tablic takie sortowanie jest dość optymalne, ponieważ nie zabiera niemal żadnej dodatkowej pamięci, a narzut związany z przesuwaniem "bąbelków" jest minimalny.
Przejdźmy do przykładu.
procedure SortowanieBabelkowe (var A: Tablica; n: Integer);
var i: Integer; j: Integer; tmp: ElementTablicy;
begin
for i:=n downto 2 do
for j:=1 to i-1 do
if A[j]>A[j+1] then
begin
tmp := A[j];
A[j] := A[j+1];
A[j+1] := tmp;
end;
end;
Jak widać, procedura sortująca, która używa sortowania bąbelkowego jest bardzo prosta. Przeanalizujmy jej działanie.
Procedura jako parametry otrzymuje tablicę przez referencję. Przekazanie przez wartość (bez słowa kluczowego var) mijałoby się z celem, ponieważ po zakończeniu działania procedury kopia tablicy zginęłaby i nie otrzymalibyśmy posortowanej tablicy. Potrzebne nam są trzy pomocnicze zmienne (to jest cała dodatkowa pamięć!). Dwie z nich to zmienne typu liczbowego, które będą służyć jako liczniki dwóch pętli. Oprócz tego potrzebna jest zmienna, w której tymczasowo będzie przechowywana wartość pola tablicy. Jej typ powinien być zatem taki sam jak typ elementów tablicy.
Teraz jedyne co nam pozostaje to przejrzeć tablicę. Dla wygody robimy to od tyłu. Najpierw przeglądamy podtablicę o rozmiarze n, potem w każdym przebiegu zewnętrznej pętli przeglądamy tablicę o jeden mniejszą. Kiedy podczas przeglądania podtablicy napotkamy sytuację, w której zaburzony zostaje porządek sąsiadujących elementów, zamieniamy sąsiadujące miejscami. Tym sposobem w jednym przebiegu pętli wewnętrznej "przepychamy" jeden bąbelek na jego miejsce.
Zmienna pomocnicza tmp jest potrzebna przy zamianie miejscami wartości A[j] i A[j+1].
Algorytm sortowania bąbelkowego można ulepszyć. Może się bowiem zdarzyć, że niektóre przebiegi pętli wewnętrznej nie są potrzebne, ponieważ część bąbelków już jest na swoim miejscu. W tym celu wprowadza się dodatkową zmienną zwaną kresem, która mówi, na którym miejscu nastąpiła zamiana. Następny przebieg pętli będzie dotyczył tego kresu.
Sortowanie przez prosty wybór, selection sort
Sortowanie przez prosty wybór jest jednym z najbardziej intuicyjnych algorytmów sortujących. W każdym kroku algorytmu zapamiętuje się kolejną pozycję i wstawia na nią element najmniejszy w jeszcze nie posortowanym ciągu, zamieniając miejscami.
Oto kod algorytmu:
procedure SortowaniePrzezProstyWybor (var A: Tablica; n: Integer);
var i:Integer; j:Integer; pozycjaMinimum:Integer; tmp:ElementTablicy;
begin
for i:=1 to n-1 do
begin
pozycjaMinimum := i;
for j:=i+1 to n do
