ALGORYTMY I STRUKTURY DANYCH
WYKŁAD 05 Problem Sortowania Grażyna Mirkowska
PJWSTK, semestr zimowy 2002
Plan wykładu
Sformułowanie problemu
Sortowanie przez porównywanie elementów
– Sortowanie przez wstawianie – Sortowanie przez selekcję
– Operacja scalania ciągów uporządkowanych – Sortowanie przez scalanie
Sformułowanie problemu
Dany jest ciąg e elementów e
1,e
2,... , e
nnależących do pewnej liniowo uporządkowanej przestrzeni <E, >.
Znaleźć taką permutację i
1, i
2,... ,i
nliczb 1,..., n aby e
i1e
i2... e
inZnaleźć taką funkcję różnowartościową i „na”
f : {1,2...,n} {e
1,e
2,... , e
n}, że dla każdego i<n, f(i) f(i+1).
5 3 1 6 7 2 8
3 6 2 1 4 5 71 2 3 5 6 7 8
1 2 3 4 5 6 7
Sortowanie przez selekcję
Metoda Sortowanie odbywa się w n-1 przebiegach. W i-tym przebiegu szukamy i-tego najmniejszego elementu.
Algorytm
{ for i := 1 to n-1 do min := i; j := i+1;
while j < n+1 do
if e[j] < e[min] then min := j fi od;
swap(e[i],e[min]);
od }
Odcinek uporządkowany
Diagram przepływu
i := 1 i < n
Znajdź x takie, że
x = minimum( e[i],..., e[n])
Zamień
miejscami x i e[i]
i := i+1
tak
nie
e[1] ... e[i-1] {e[i],...,e[n]}
x {e[i],...,e[n]}
e[1] ... e[i-1] e[i] {e[i+1],...,e[n]}
e[1] ... e[i-1] {e[i],...,e[n]}
Koszt algorytmu
Twierdzenie Algorytm Selection_sort jest poprawnym
rozwiązaniem problemu sortowania. W dowolnej strukturze danych, której elementy są liniowo uporządkowane przez relację , algorytm zatrzymuje się dla dowolnych danych i daje w wyniku ciąg uporządkowany niemalejąco.
A. Jeśli operacją dominującą jest porównywanie elementów:
T(n) = n-1 + n-2 + ... +2 + 1 = n(n-1)/2 = (n
2)
B. Jeśli operacją dominującą jest zamiana elementów
T(n) = 1*(n-1) = n-1 = (n)
Sortowanie przez wstawianie
Sortowanie odbywa się w n -1 przebiegach. W i-tym przebiegu elementy na pozycjach 1...(i-1) są już uporządkowane, a
wstawiamy i-ty element przepychając go do przodu na właściwe miejsce, tak by stworzył wraz z innymi ciąg uporządkowany długości i.
Odcinek uporządkowany
i-ty element
X
4 8 5 3 9 6 4 5 8 3 9 6
5
4 5 3 8 9 6 4 3 5 8 9 6 3 4 5 8 9 6
3
9
3 4 5 8 9 6 6
itd
4 8
Schemat algorytmu
i := 2
i < n+1
Umieść e[i] wśród elementów e[1],e[2],...e[i-1],
przesuwając elementy większe o jedno miejsce w prawo, tak by ciąg i-pierwszych elementów był uporządkowany
start
Tak
Nie
i := i+1
stop
e[1]... e[i-1] , i < n+2
e[ 1]... e[i-1] , i<n +1
e[ 1]... e[i-1] e[i] , i<n +1 e[ 1]... e[i-2] e[i-1],
i<n +2
Algorytm Insertion_sort
{for i := 2 to n do
j := i; pom := e[i];
while ( j>1 andif e[j-1]> pom ) do
e[j] := e[j-1];
j := j-1 od;
e[j] := pom od}
e[ 1]... e[i-1]
e[ 1]... e[j-1] pom=e[i], j=i
pom< e[ j+1]... e[i], pom <e[j-1]
Pom < e[ j]e[j+1]... e[i]
pom< e[ j+1]... e[i]
e[ 1] ... e[j-1] pom< e[ j+1]... e[i]
e[ 1] ... e[j-1] e[j] < e[ j+1]... e[i]
Poprawność sortowania przez wstawianie
Algorytm sortowania przez wstawianie poprawnie rozwiązuje problem sortowania w każdej liniowo uporządkowanej strukturze danych.
Algorytm sortowania przez wstawianie jest, w każdej liniowo uporządkowanej strukturze
danych, całkowicie poprawny ze względu na
warunek początkowy n>0 i warunek końcowy
(1<i n) e[i-1] e[i] .
Koszt sortowania przez wstawianie
W(n) = i=2...n (koszt maksymalny pętli wewnętrznej) =
i=2...n(i-1) = n(n-1)/2 = O(n
2)
A(n) = i=2...n (koszt średni pętli wewnętrznej)=
Element pom z
prawdopodobieństwem 1/i może zająć dowolną z pozycji
od 1 do i.
Operacja dominująca - porównywanie elementów.
= i=2...n ( j=1...i j*(1/i)) = i=2...n (1/i)(i (i+1))/2 =
(n+1)(n+2)/4 - 1.5 = (1/4)n
2 +O(n)
j*(1/i)) = i=2...n (1/i)(i (i+1))/2 =
(n+1)(n+2)/4 - 1.5 = (1/4)n
2 +O(n)
Sortowanie przez scalanie
(1) Dzielimy zadanie posortowania całego ciągu na dwa podzadania: posortowania jego lewej i prawej połowy.
(2) Gdy obie części tworzą już ciągi
uporządkowane, wtedy scalamy je otrzymując
rozwiązanie.
Przykład
16 5 12 4 10 6 1 13 15 7 1 14 9 3 8 11
16 5 12 4 10 6 1 13 15 7 1 14 9 3 8 11 16 5 12 4 10 6 1 13
16 5 12 4 5 16 4 12 4 5 12 16
10 6 6 10
1 6 10 13 1 13 1 13
1 4 5 6 10 12 13 16
15 7 1 14 9 3 8 11
1 3 7 8 9 11 14 15
1 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Operacja scalania
Dane są dwa ciągi X i Y, uporządkowane niemalejąco, x1,...xn i y1,...ym. Utworzyć ciąg e=e1,...e n+m złożony z elementów obu ciągów uporządkowany niemalejąco.
Wp = {n>0 m>0, x1... xn i y1... ym }
Wk = { e1... en+m , (in+m)( j)( ei=xj lub ei =yj)}
1
2 4 5 8 9 1 3 6 7
2 3 4 5 6 7 8 9
Algorytm scalania
{i:=1; j := 1;
k :=1,
while (i n and j m) do if x[i]< y[j] then
e[k] := x[i];
i := i +1 else
e[k] := y[j];
j := j +1 fi;
k := k+1;
od;
if ( j > m) then for i := i to n do
e[k] := x[i]; k := k+1 od
Else
for j := j to m do
e[k] := y[j]; k := k+1 od}
{k= i+j-1, e[1]... e[k-1] i wszystkie elementy x[1],...,x[i-1] oraz y[1],...,y[j-1]
zostały już umieszczone na pozycjach od 1 do k-1 w ciągu e .}
O(n+m)
Specyfikacja procedury scal(k,x,l)
Wersja procedury scal (k,x,l) użyta w algorytmie Sortowania przez scalanie ma następującą specyfikację
Wp = {k x l e[k] e[k+1] … e[x]
e[x+1] e[x+2] … e[l]}
Wk = {e[k] e[k+1] …e[x] e[x+1] … e[l] }
Twierdzenie (*)
Procedura scal(k, x,l) zastosowana do dowolnego
ciągu e[1],...,e[n] jest całkowicie poprawna ze
względu na podaną wyżej specyfikację.
Sortowanie przez scalanie
procedure MS(k,l : integer);
begin
if l>k then
x := (k+l) div 2;
MS(k,x);
MS(x+1,l);
scal (k,x,l) fi
end MS;
Jeśli k=l, to jest tylko jeden element w naszym ciągu.
W tym wywołaniu rozważamy lewą „połowę” danego ciągu
Z założenia indukcyjnego : e[k] ... e[x]
W tym wywołaniu rozważamy prawą „połowę” danego ciągu Z założenia indukcyjnego :e[x+1] ... e[l]
Na mocy Tw (*) : e[k] ... e[l]
Koszt algorytmu Merge_Sort
Załóżmy, że n = 2 p. wtedy T(n) = T(n/2) + T(n/2) + cn T(1) = 0
T(n) = (n lg (n))
Po podstawieniu mamy T(2 0) = 0
T(2 p) = 2 T(2 p-1) +c n T(2 p ) = 2 T(2 p-1) + c2 p = 2(2T(2 p-2) +c 2 p-1) + c2 p =
2 2 T(2p-2 ) + c2 p + c2 p = ...= 2 p T(2 0 ) + cp 2 p =