Algorytmy i Struktury Danych, 3. ćwiczenia
2010-10-19
1 Plan zajęć
• ShellSort: k-sortowanie zachowuje h-posortowanie, algorytm Pratta z kro- kami postaci 2p3q
• heapsort – budowa kopca
• quicksort
• średnia liczba porównań
2 Sortowanie metodą Shella, dowody
Lemat 1 Niech m, n, r będą nieujemnymi liczba całkowitymi i niech (x1, . . . , xm+r) oraz (y1, . . . , yn+r) będą dowolnymi ciągami liczbowymi takimi, że yi≤ xm+idla 1 ≤ i ≤ r. Jeśli elementy x oraz y posortujemy niezależnie tak, że x1 ≤ . . . ≤ xm+r oraz y1≤ . . . ≤ yn+r to nadal będziemy mieli yi≤ xm+i dla 1 ≤ i ≤ r.
Po posortowaniu element xm+ijest większy bądź równy od co najmniej m+i elementów z x, wśród nich jest co najmniej i elementów które przed sortowaniem były na pozycjach m, . . . , m + r, każdy z tych elementów ma wśród y element od którego jest większy, stąd xm+ijest większy bądź równy od i najmniejszych elementów y.
(pełny dowód jest w Knuth, tom III, strona 94) qued
Lemat 2 Jeśli tablica jest h posortowana i k posortujemy, to nadal będzie h posortowana.
Niech ai i ai+h elementy które po sortowaniu nie są h posortowane. Niech Y ciąg zawierający ai, ai+k, ai+2k, . . .. Niech X ciąg zawierający ai+h, ai+h+k, ai+h+2k, . . .. Po k posortowaniu ciągi Y i X są uporządkowane, z poprzedniego
lematu mamy jednak, że ai≤ ai+h— sprzeczność. qued
Lemat 3 Liczba porównań wymagana przy h posortowaniu tablicy rozmiaru n wynosi O(n2/h).
Mamy h ciągów, każdy o długości n/h — stąd całkowity czas wynosi h ·
n2/h2= n2/h. qued
1
Lemat 4 Liczba porównań wymagana przy hiposortowaniu tablicy rozmiaru n, która jest hi+1 i hi+2 posortowana wynosi O(nhi+1hi+2/hi) (przy założeniu, że hi+1 i hi+2 są względnie pierwsze)
Trzeba pokazać, że jeśli ciąg jest hi+1 i hi+2 posortowany, to jeśli k ≥
hi+1hi+2, to ai≤ ai+k. qued
Lemat 5 Dla ciągu h = {2i− 1 : i ∈ N } algorytm ShellSort ma złożoność O(n√
n).
(Knuth, tom III, strona 95)
Niech Bi koszt i–tej fazy, t = dlog ne. Dla pierwszy t/2 przebiegów h ≥ sqrtn, ponieważ koszt jednej fazy jest ograniczona przez O(n2/h) stąd suma- ryczny koszt jest rzędu O(n1.5). Dla pozostałych przebiegów możemy skorzystać z poprzedniego lematu, koszt pojedynczej fazy jest równy Bi = O(nhi+2hi+1/hi),
więc sumaryczny koszt tych faz jest również rzędu O(n1.5). qued
Lemat 6 Dla ciągu h = {2i3j : i, j ∈ N } algorytm ShellSort ma złożoność O(n log2n).
Wszystkich faz algorytmu jest O((log n)2). Trzeba pokazać, że każda z nich zajmuje O(n) czasu. Obserwacja — jeśli ciąg jest 2 i 3 uporządkowany, to jego 1–posortowanie wymaga O(n) czasu. Analogicznie jeśli ciąg jest 2i i 3i posorto-
wany to jego i posortowanie wymaga O(n). qued
3 Algorytm generowania kroków postaci 2
p3
qon- line
Algorytm 1: GenSequence() h = emptyHeap(); seq = ∅ ;
1
h.add(1); ;
2
while true do
3
x = h.extractM in() ;
4
seq.add(x) ;
5
h.add(3 · x) ;
6
if h mod 3 6= 0 then
7
h.add(2 · x) ;
8
4 Budowa kopca w algorytmie HeapSort
Kopiec możemy budować idąc od dołu do góry. Zauważmy, że ostatnie n/2 ele- mentów spełnia warunki kopca, pozostaje jedynie poprawić porządek w pierw- szych n/2 elementach.
2
for i = bn/2c downto 1 do DownHeap(i)
end for
Koszt budowy kopca możemy opisać wzorem:
n 4 · 2 +n
8 · 4 + n
16· 6 + . . . = n ·X
i=1
i 2i =
n · (X
i=1
1 2i +X
i=2
1 2i +X
i=3
1
2i + . . .) = n · (1 +1 2 +1
4+ . . .) = 2n
5 Analiza algorytmu QuickSort
(AiSD, strony 56-58)
Dla losowej permutacji, oczekiwany czas sortowania przy użyciu algorytmu Qu- ickSort wynosi:
T (n) =
(1 dla n ≤ 1
(n + 1) +n1Pn
i=1(T (i − 1) + T (n − i)) dla n > 1 Dla n > 1 możemy równanie przekształcić do:
T (n) = (n + 1) +2 n
n
X
i=1
T (i − 1) (mnożymy obie strony przez n)
nT (n) = n(n + 1) + 2
n
X
i=1
T (i − 1) dla n − 1 otrzymujemy:
(n − 1)T (n − 1) = (n − 1)n + 2
n−1
X
i=1
T (i − 1) po odjęciu stronami otrzymujemy:
T (n) = n + 1
n T (n − 1) + 2 co możemy przekształcić do:
T (n)
n + 1 =T (n − 1)
n + 2
n + 1 po przesumowaniu obu stron dla n0= 1..n
T (n)
n − 1= T (0)
1 +
n
X
i=1
2 i + 1 Stosując przekształcenia możemy otrzymać:
T (n) = 2(n + 1)(Hn+ 1 n + 1−3
2)
3
6 QS bez stosu przy sortowaniu dodatnich liczb całkowitych
Zakładamy, że w tablicy możemy przechowywać liczby ujemne, i znak liczby będziemy traktować jako dodatkowy bit informacji.
l=0; r=n ;
1
while l < n do
2
// niezmienniki: A[l . . . (r − 1)] > 0, ;
3
// liczby z ujemny znakiem są już posortowane ;
4
while l < r do
5
m=Partition(l, r) ;
6
A[m] = −A[m] ;
7
r = m
8
while l < n and A[l] < 0 do l = l + 1 ;
9
r = l + 1 ;
10
while r < n and A[r] > 0 do r = r + 1
11
4