ASD Ćwiczenia 8: Różnice pomiędzy wersjami
mNie podano opisu zmian |
Nie podano opisu zmian |
||
(Nie pokazano 7 wersji utworzonych przez 2 użytkowników) | |||
Linia 9: | Linia 9: | ||
Pokażemy, że drzewo AVL o wysokości <math>h</math> musi mieć rozmiar wykładniczy względem <math>h</math>, konstruując drzewo AVL <math>T_h</math> o wysokości <math>h</math> i najmniejszym możliwym rozmiarze. Jego definicja jest rekurencyjna: | Pokażemy, że drzewo AVL o wysokości <math>h</math> musi mieć rozmiar wykładniczy względem <math>h</math>, konstruując drzewo AVL <math>T_h</math> o wysokości <math>h</math> i najmniejszym możliwym rozmiarze. Jego definicja jest rekurencyjna: | ||
[[grafika:Fib_tree.png]] | |||
Oznaczmy rozmiar drzewa <math>T_h</math> przez <math>a_h</math>. Mamy | Oznaczmy rozmiar drzewa <math>T_h</math> przez <math>a_h</math>. Mamy | ||
<math>a_0 = 1</math>, <math>a_1 = 2</math>, <math>a_{h+1} = a_h+a_{h-1}+1</math> dla <math>h>0</math>. Porównując kilka pierwszych wyrazów tego ciągu z początkowymi wyrazami ciągu Fibonacciego <math>\langle F_h\rangle</math> zgadujemy, że <math>a_h = | <math>a_0 = 1</math>, <math>a_1 = 2</math>, <math>a_{h+1} = a_h+a_{h-1}+1</math> dla <math>h>0</math>. Porównując kilka pierwszych wyrazów tego ciągu z początkowymi wyrazami ciągu Fibonacciego <math>\langle F_h\rangle</math> zgadujemy, że <math>a_h = F_{h+3}-1</math> (co łatwo sprawdzić przez indukcję) - dlatego <math>T_h</math> noszą nazwę '''drzew Fibonacciego'''. Mamy zatem <math>a_h = \Theta(\phi^h)</math>. | ||
</div> | </div> | ||
</div> | </div> | ||
Linia 36: | Linia 36: | ||
Zadanie 4 | Zadanie 4 | ||
Chcemy, żeby nasz słownik udostępniał dodatkową operację Select(S,k), zwracającą k-ty najmniejszy element w S. Jak zmodyfikować drzewa AVL, żeby pozwalały wykonywac operację Select w czasie logarytmicznym? | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"> | |||
Rozwiązanie | |||
<div class="mw-collapsible-content" style="display:none"> | |||
W każdym węźle przechowujemy atrybut 'rozmiar poddrzewa'. Taki atrybut można łatwo uaktualniać podczas rotacji i pozwala on na zlokalizowanie k-tego elementu przy pomocy jednego zejścia od korzenia w dół drzewa. | |||
</div> | |||
</div> | |||
Zadanie 5 | |||
Udowodnij Lemat 1. | Udowodnij Lemat 1. | ||
Linia 43: | Linia 56: | ||
<div class="mw-collapsible-content" style="display:none"> | <div class="mw-collapsible-content" style="display:none"> | ||
.................. | Niech <math>\mu(r)</math> i <math>\mu'(r)</math> oznaczają wartość funkcji <math>\mu</math> dla węzła <math>r</math>, odpowiednio, przed i po operacji splay. Dalej, niech <math>v</math>, <math>w</math> i <math>u</math> oznaczają węzły jak w definicji operacji splay. Rozważamy trzy przypadki, odpowiadające trzem podpunktom w tej definicji. | ||
(1) Węzeł <math>u</math> nie istnieje, bo <math>v</math> jest synem korzenia <math>w</math>. <br> | |||
Do wykonania mamy tylko ROT1<math>(v, w)</math>, a do zużycia <math>3(\mu'(v)-\mu(v))+1</math> jednostek kredytu. Mamy <math>\mu'(v) = \mu(w)</math> oraz <math>\mu'(w) \le \mu'(v)</math>.<br> | |||
Na utrzymanie niezmiennika (***) potrzeba <br> | |||
<math>\mu'(v)+\mu'(w)-\mu(v)-\mu(w) = \mu'(w)-\mu(v) \le \mu'(v)-\mu(v) \le 3(\mu'(v)-\mu(v)</math><br> | |||
jednostek kredytu. Pozostałą 1 jednostkę przeznaczamy na opłacenie wykonania rotacji. | |||
(2) Oba węzły <math>v</math> i <math>w</math> są lewymi synami (albo oba prawymi). <br> | |||
Wykonujemy ROT1<math>(w,u)</math>, a następnie ROT1<math>(v,w)</math>. Pokażemy, że do wykonania tych dwóch rotacji i utrzymania niezmiennika (***) wystarczy <math>\le 3(\mu'(v)-\mu(v)</math> jednostek kredytu (podobnie jak w przypadku (3)). Sumując to oszacowanie po wszystkich operacjach wykonywanych w ramach procedury splay, dostajemy sumę teleskopową jak w tezie lematu. | |||
Mamy <math>\mu'(v) = \mu(u)</math>. Na utrzymanie niezmiennika (***) potrzeba <br> | |||
<math>\Delta\mu = \mu'(v)+\mu'(w)+\mu'(u)-\mu(v)-\mu(w)-\mu(u) = \mu'(w)+\mu'(u)-\mu(v)-\mu(w) = (\mu'(w)-\mu(v)) + (\mu'(u)-\mu(w)) \le 2(\mu'(v)-\mu(v))</math><br> | |||
jednostek kredytu, co pozostawia <math>\mu'(v)-\mu(v)</math> jednostek na opłacenie wykonania dwóch rotacji. Może się jednak zdarzyć, że <math>\mu'(v)=\mu(v)</math>. Pokażemy, że wtedy <math>\Delta\mu <0</math>, więc utrzymujemy niezmiennik (***), odbierając jednostkę kredytu na opłacenie rotacji. | |||
Przypuśćmy przeciwnie, że <math>\mu'(v)=\mu(v)</math>, ale <math>\mu'(v)+\mu'(w)+\mu'(u)\ge\mu(v)+\mu(w)+\mu(u)</math>. Mamy <math>\mu(u) = \mu'(v) = \mu(v)</math>, skąd <math>\mu(w) = \mu(v) = \mu(u)</math>, więc <math>\mu'(v)+\mu'(w)+\mu'(u)\ge 3\mu(u) = 3\mu'(v)</math>, czyli <math>\mu'(w)+\mu'(u)\ge 2\mu'(v)</math>. Ponieważ <math>\mu'(w), \mu'(u)\le\mu'(v)</math>, to wnioskujemy, że również <math>\mu'(w) = \mu'(v) = \mu'(u)</math>. To jednak jest niemożliwe: niech bowiem <math>a</math> oznacza rozmiar poddrzewa o korzeniu <math>v</math> przed operacją, zaś <math>b</math> - rozmiar poddrzewa o korzeniu <math>u</math> po operacji. Mielibyśmy wówczas równość <math>\lfloor \lg a\rfloor = \lfloor \lg (a+b+1)\rfloor = \lfloor \lg b\rfloor</math>. Załóżmy dla ustalenia uwagi, że <math>a\le b</math>; mamy <math>\lfloor \lg (a+b+1)\rfloor\ge\lfloor \lg (2a)\rfloor = 1+\lfloor \lg a\rfloor > \lfloor \lg a\rfloor</math>, sprzeczność. | |||
(3) Węzeł <math>v</math> jest lewym synem, a <math>w</math> prawym, albo odwrotnie. | |||
Dowód analogiczny jak w przypadku (2). | |||
</div> | </div> | ||
</div> | </div> | ||
Zadanie | Zadanie 6 | ||
Opisane tu operacje Insert i Delete dla B-drzew są ''dwuprzebiegowe'': najpierw schodzimy od korzenia do liścia, a następnie wracamy w górę drzewa przywracając warunki równowagi. Zaprojektuj wymagające z grubsza o połowę odwołań do dysku mniej ''jednoprzebiegowe'' algorytmy wstawiania i usuwania kluczy z B-drzewa, w których przywracanie równowagi odbywa się od razu podczas marszu w dół drzewa. | Opisane tu operacje Insert i Delete dla B-drzew są ''dwuprzebiegowe'': najpierw schodzimy od korzenia do liścia, a następnie wracamy w górę drzewa przywracając warunki równowagi. Zaprojektuj wymagające z grubsza o połowę odwołań do dysku mniej ''jednoprzebiegowe'' algorytmy wstawiania i usuwania kluczy z B-drzewa, w których przywracanie równowagi odbywa się od razu podczas marszu w dół drzewa. | ||
Linia 57: | Linia 89: | ||
<div class="mw-collapsible-content" style="display:none"> | <div class="mw-collapsible-content" style="display:none"> | ||
Podstawowy pomysł polega na zadbaniu o to, by przy wstawianiu ojciec aktualnego węzła nigdy nie był całkowicie wypełniony, a przy usuwaniu - miał przynajmniej o jeden klucz więcej niż dozwolone minimum. Szczegóły można znaleźć w książce [CLRS], podrozdz. 18.2 i 18.3. | Podstawowy pomysł polega na zadbaniu o to, by przy wstawianiu ojciec aktualnego węzła nigdy nie był całkowicie wypełniony, a przy usuwaniu - miał przynajmniej o jeden klucz więcej niż dozwolone minimum. Szczegóły można znaleźć w książce [CLRS], podrozdz. 18.2 i 18.3. | ||
</div> | |||
</div> | |||
Zadanie 7 | |||
Chcemy rozszerzyć nasz zestaw operacji słownikowych o dwie dodatkowe: <br> | |||
Join(S1,S2) łączy dwa słowniki w jeden, przy założeniu, że wszystkie klucze w S1 są mniejsze niż wszystkie klucze w S2; <br> | |||
Split(S,x) dzieli słownik S na dwa słowniki: pierwszy złożony z elementów mniejszych bądź równych x i drugi zlożony z elementów większych od x.<br> | |||
Jak zrealizować te operacje<br> | |||
(a) dla B-drzew;<br> | |||
(b) dla drzew AVL<br> | |||
z kosztem proporcjonalnym do wysokości przetwarzanych drzew? | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"> | |||
Rozwiązanie | |||
<div class="mw-collapsible-content" style="display:none"> | |||
(a) Join w B-drzewach można zrealizować następująco: | |||
Niech h1 i h2 oznaczają wysokości obu drzew; dla ustalenia uwagi załóżmy, że <math>h1 > h2</math>. Schodzimy w S1 do węzła o wysokości <math>h2+1</math>, dołączamy S2 jako jego skrajnie prawego syna i poprawiamy zrównoważenie jak przy wstawianiu.<br> | |||
W celu wykonania Split schodzimy do węzła z kluczem $x$, po czym wracamy do korzenia, wykonując Join osobno dla drzew po lewej i po prawej stronie ścieżki. Koszt Join jest w zasadzie proporcjonalny do ''różnicy wysokości'' łączonych drzew, więc łączny koszt wyjdzie proporcjonalny do wysokości $S$. | |||
(b) W przypadku AVL idea jest podobna jak poprzednio, ale pojawia się trochę problemów technicznych; szczegółowe rozwiązanie można znaleźć na przykład w książce D. Knutha "Sztuka programowania", WNT 2002, podrozdz. 6.2.3, str. 509. | |||
</div> | </div> | ||
</div> | </div> |
Aktualna wersja na dzień 10:09, 31 sie 2023
Zadanie 1
Udowodnij, że wysokość drzewa AVL jest logarytmiczna względem jego rozmiaru.
Rozwiązanie
Zadanie 2
Oblicz, ile jest różnych drzew Fibonacciego o wysokości .
Rozwiązanie
Zadanie 3
Napisz pseudokod operacji Insert i Delete w drzewach AVL.
Zadanie 4
Chcemy, żeby nasz słownik udostępniał dodatkową operację Select(S,k), zwracającą k-ty najmniejszy element w S. Jak zmodyfikować drzewa AVL, żeby pozwalały wykonywac operację Select w czasie logarytmicznym?
Rozwiązanie
Zadanie 5
Udowodnij Lemat 1.
Rozwiązanie
Zadanie 6
Opisane tu operacje Insert i Delete dla B-drzew są dwuprzebiegowe: najpierw schodzimy od korzenia do liścia, a następnie wracamy w górę drzewa przywracając warunki równowagi. Zaprojektuj wymagające z grubsza o połowę odwołań do dysku mniej jednoprzebiegowe algorytmy wstawiania i usuwania kluczy z B-drzewa, w których przywracanie równowagi odbywa się od razu podczas marszu w dół drzewa.
Rozwiązanie
Zadanie 7
Chcemy rozszerzyć nasz zestaw operacji słownikowych o dwie dodatkowe:
Join(S1,S2) łączy dwa słowniki w jeden, przy założeniu, że wszystkie klucze w S1 są mniejsze niż wszystkie klucze w S2;
Split(S,x) dzieli słownik S na dwa słowniki: pierwszy złożony z elementów mniejszych bądź równych x i drugi zlożony z elementów większych od x.
Jak zrealizować te operacje
(a) dla B-drzew;
(b) dla drzew AVL
z kosztem proporcjonalnym do wysokości przetwarzanych drzew?
Rozwiązanie