Drzewa binarne Mateusz Bednarski 117194, Nikodem Hynek 117209 10 kwietnia 2014 1 Wprowadzenie Celem niniejszej pracy jest przeanalizowanie dwóch drzew binarnych. Drzewa BST (Binary Search Tree) oraz ich odmiany w postaci drzew AVL (Adelson-Velskii and Landis' ). Struktury te s bardzo podobne w zewn trzenej budowie, ró»ni si jednak znacz co sposobem implementacji. Na obu drzewach zaimplementowano nast puj ce operacje: Tworzenie drzewa Dodawanie elementów Wyszukiwanie Znajdowanie elementu minimalnego Wypisywanie metod in-order (rosn co) Zwalnianie drzerwa (post-order) Zwracanie warto±ci korzenia Usuwanie elementów Balansowanie drzewa(tylko drzewo BST) 2 Metodyka testów Pomiary czasów wykonania zostaªy przeprowadzone na komputerze wyposa»onym w 8GB pami ci RAM, procesor Intel Core i5 taktowany z cz stotliwo±ci 3,1GHz. Platforma systemowa: Windows 8.1 x64. Kompiltor MSVC++ w wersji 2013. Tryb kompliacji Release. Dodatkowe opcje kompilacji: favor fast code, omit frame pointers. Do pomiaru czasu wykonania zostaªa wykorzystana nast puj ca funkcja (wraz z opdpwiednimi przeci»eniami dla innych parametrów). 1
Listing 1: PerfomanceCounter.cpp (fragment) double PerfomanceCounter : : MeasureExecutionTime ( std : : function <void ( void)> func ) const { LARGE_INTEGER start, stop ; QueryPerformanceCounter(& s t a r t ) ; func ( ) ; } QueryPerformanceCounter(& stop ) ; return ( stop. QuadPart s t a r t. QuadPart ) 1.0 / frequency ; Wykorzystana zostaªa funkcjonalno± Windows zapewniani ca zegar wysokiej cz stotliwo±ci. Narzut czasowy funkcji pomiarowej wynosi okoªo 10 6 s - jest wi c pomijalnie maªy. Ka»dy pomiar zostaª powtórzony 100 razy, wyniki zostaªy u±rednione, oraz zostaªy podane odchylenia standardowe. 3 Drzewo BST Konstrukcja drzewa BST jest bardzo prosta. Ka»dy w zeª ma dwóch potomków. Lewy potomek jest mniejszy od rodzica, prawy jest wi kszy. Duplikaty s automatycznie usuwane. Drzewo nie balansuje si samodzielnie w»aden sposób. Konieczne jest jego jawne balansowanie. Wszystkie zªo»ono±ci zostaªy podane w przypadku ±rednim. W przypadku pesymsitycznym wynosz O(n). 3.1 Dodawanie Listing 2: Dodawanie w zªa w drzewie BST type r e f = w zeª ; w zeª = record klucz : i n t e g e r ; lewe, prawe : r e f end ; var n : i n t e g e r ; korze«: r e f ; function drzewo (n : i n t e g e r ) : r e f ; var nowyw zeª : r e f ; x, nl, np : i n t e g e r ; begin i f n=0 then drzewo:= n i l e l s e begin n l :=n div 2; np:=n nl 1; read ( x ) ; new( nowyw zeª ) ; with nowyw zeª do begin klucz := x ; lewe :=drzew ( nl ) ; prawe = drzewo (np) end ; drzewo := nowyw zeª end end{drzewo }. 2
Zªo»ono± operacji dodawania wynosi O(log n). Z t zªo»ono±ci spotkamy si jeszcze wiele razy. 3.2 Najmniejszy element Listing 3: Znajdowanie najmniejszego elementu procedure minimum( t : r e f ) begin i f t = n i l then w r i t e l n (" Puste drzewo ") begin while ( t n i l ) do minimum( t. lewe ) ; P( t ) ; minimum( t. prawe ) ; end end Procedura jest bardzo prosta. Schodzimy w lewo tak dªugo, dopóki s jeszcze lewi synowie. Zªo»ono± - O(log n). 3.3 Wypisanie rosn co Wypisanie rosn co sprowadza si do przej±cia drzewa w kolejno± in-order (lewy syn, w zeª, prawy syn). Zªo»ono± tradycyjnie O(log n). procedure inorder ( t : r e f ) ; begin i f t n i l then begin poprzeczny ( t. lewe ) ; Wypisz ( t ) ; inorder ( t. prawe ) ; end end Listing 4: Przej±cie in-order 3
3.4 Usuwanie procedure usu«( x : i n t e g e r ; var p : r e f ) ; var q : r e f ; procedure us ( var r : r e f ) ; begin i f r. prawe n i l then us ( r. prawe ) e l s e begin q. klucz := r. klucz ; q. l i c z n i k := r. l i c z n i k ; q:= r ; r := r. lewe end end ; begin {usu«} i f p = n i l then w r i t e l n ("BRAK WARTO CI W DRZEWIE") e l s e i f x<p. klucz then usu«(x, p. lewe ) e l s e i f x>p. klucz then usu (x, p. prawe ) e l s e begin { usu«p } q:=p ; i f q. prawe = n i l then p:=q. lewe e l s e i f q. lewe = n i l then p:=q. prawe e l s e us ( q. lewe ) ; { dispose ( q)} end end {usu«} Usuwanie jest nieco bardziej skomplikowane, gdy» musimy zmpdykowa drzewo tak aby zachowaªo wªasno± BST. Mimo to zªo»ono± tej operacji wynosi O(log n). 3.5 Równowa»enie (wywa»anie) Listing 5: Równowa»enie drzewa. Faza I TworzKregoslup ( w zeª korzen, l i c z b a caªkowita n) tmp = korzen ; //tmp to zmienna tymczasowa while tmp nie j e s t równe NULL i f tmp posiada lewego potomka wykonaj r o t a c j tego potomka wzgl dem tmp ; tmp z o s t a j e przesuni ty do nowo powstaªego rodzica ; e l s e tmp z o s t a j e przesuni ty w miejsce swojego prawego potomka ; Listing 6: Równowa»enie drzewa. Faza II TworzWywazoneDrzewo ( l i c z b a caªkowita n) m = 2 [log 2 (n+1)] 1 ; wykonaj n m r o t a c j i, zaczynaj c od pocz tku l i n i i ; while m > 1 m = [m/ 2 ] ; // znaczenie nawiasów [ ] jak wy»ej wykonaj m r o t a c j i, s t a r t u j c od pocz tku l i n i i ; Wywa»anie drzewa skªada si z dwóch faz. W pierwszej drzewo jest redukowane do listy jednokierunkowej, opieraj c si na prawych synach. W drugiej owa lista jest skªadana z powrotem w drzewo, tym razem zbalansowane. 4
Zªo»ono± pierwszej fazy wynosi O(n) a drugiej O(n) co daje w sumie O(n + n) O(n). 4 Drzewa AVL Drzewo AVL jest drzewem BST wraz z pewn dodatkow wªasno±ci. Mianowicie jest ono wywa»ane za ka»dym razem po zmianie jego zawarto±ci. Wydªu»a to nieco czas dodawania/usuwania ale za to drzewo jest zawsze idealnie wywa»one. Nie wyst ouje tutaj jawne balansowanie, a metody wypisania rosn co i znajdowania minimalnego elementu s identyczne jak w drzewie BST wi c nie zostaªy tutaj podane. 4.1 Rotacje W trakcie dodawania lub usuwania elementów zachodzi potrzeba rotowania w zªów. Algortym w pseudokodzie jest maªo zytelny, wi c autorzy zdecydowali si na zamieszczenie schematów tych»e rotacji. 4.2 Dodawanie Algo here Listing 7: Dodawanie elementu do drzewa AVL Jak wida dodawanie jest bardziej skomplikowane ni» w drzewie BST. Mimo to nadal osi gamy znakomite O(log n). 4.3 Usuwanie Algo here Listing 8: Usuwanie elementu z drzewa AVL Usuwanie jest jeszcze bardziej skomlikowane. Jest to jednak rozs dna cena za peªn równowag. Tradycyjnie O(log n). 5 Testy Wysztkie wykresy zawieraj o± OY wyskalowan logarytmicznie. 5.1 Tworzenie drzewa Do pomiaru czasu tworzenia drzewa zostaª u»yty ciag liczb pseudolosowych (z powtórzeniami, aby sprawdzi zachowanie przy pojawieniu si takich samych warto±ci - omawiane struktury usuwaj je z drzewa). 5
Rysunek 1: Rotacje AVL 6
Wykres ukªada si w bardzo ªadn krzyw y = log n. Drzewo AVL jest nieznacznie wolniejsze ze wzgl du na konieczno± balansowania. 7
5.2 Wyszukiwanie najmniejszego elementu Wykres wygl da bardzo podbnie, jednak»e z wi kszymi odchyleniami. Ich ¹ródªem jest niezwykle krótki czas wykonania - pr dzej ko«czy si pami ni» algorytm dziaªa w czasie porównywalnym do sekundy. 8
5.3 Wypisanie drzewa rosn co Równie» tutaj krzywa jest bardzo bliska funkcji logarytmicznej. W kodzie testowym pomini to wypisywanie na ekran z prostej przyczyny - czas komunikacja z konsol jest o kilka rz dów wielko±ci dªu»szy ni» czas przechodzenia drzewa. Jest on dominuj cy przez co zªo»ono± faªszywie wygl da na O(n). 9
5.4 Balansowanie drzewa BST Balansowanie drzewa BST równie» cechuje si wykresem log n. Na uwag zasªuguj dwa fakty - czas balansowania nawet du»ych drzew jest bardzo krótki. Drug rzecz jest stosunkowo du»y rozrzut - wynika on z faktu»e czas balansowania zale»y mocno od budowy drzewa. A ta jest ró»na dla ró»nych losowa«. 6 Wnioski Pierwszym wnioskiem który nasuwa si po analizie pracy jest niezwykªa wydajno± drzew. Wszystkie operacje s wykonywanie niezwykle szybko. Niemo»liwe okazaªo si wygenerowanie takiej ilo±ci danych aby czas dziaªania programu byª uci»liwy - zanim do tego doszªo ko«czyªa si pami. Drzewa BST okazaªy si niezwykle proste w implementacji. Autorom udaªo si to zrobi z gªowy bez si gania do profesjonalnej literatury. Jednak»e drzewa AVL okazaªy si bardzo trudne w implementacji. Doszli±my do wniosku,»e nie warto stosowa drzew AVL je»eli nie jest to absolutnie konieczne. Drzewa BST balansowane co jaki± czas s wystarczajaco wydajne, a dodatkowo prostota ich implementacji znacz co zmniejsza ryzyko popeªnienia bª du 1. Ró»ne rodzaje drzew znalazªy ró»ne zastosowania, zdaniema utorów sªusznie, gdy» ich idea doskonale sprawdza si w praktyce. Przykªadowymi zastosowaniami s gotowe kontenery typu set (zbiór) czy map/dictionary (mapa/sªownik). Ponadto systemy baz danych oraz sytemy plików intensywnie u»ywaj drzew do gromadzenia informacji. 1 Lepszy jest program dziaªaj cy poprawnie ale wolniej, ni» dziaª jacy szybko ale daj cy faªszywe wyniki 10
7 Bonus 7.1 Wst p Zadaniem dodatkowym byªo zaimplementowanie drzew czewono-czarnych. Autorzy dokonali tego na podstawie pozycji Algorytmy. Robert Sedgewick, Kevin Wayne. Autorzy przezentuj je jako szczególny przypadek drzewa 2-3 lub BST. W tym rodzaju drzew ka»dy w zeª ma tak jak poprzednio dwóch potomków, ponadto jest oznaczony jako czerwony lub czarny. Aby drzewo czerwono-czarne byªo poprawne musi speªnia nast puj ce warunki 2 : 1. Mysi by poprawnym drzewem BST (speªnia warunek BST tj, lewy potomek mniejszy od warto±ci w zªa a ten mniejszy od prawego potomka). 2. W zªy czerwone znajduj si po lewej stronie 3. aden w zeª nie jest powi zany z dwoma czerwonymi w zªami 4. Ka»da ±cie»ka z korzenia do li±cia ma dokªadnie tak sam liczb czarnych w zªów Ostatni warunek wymaga krótkiego komentarza. W drzewach czerwono-czarnych warto±ci przechowuje si tylko w w zª ch, li±cie s zawsze puste i czarne (zwyczajowo nie towrzy si pustego li±cia de facto, wystarcza warto± null). Drzewa te nie s perfekcyjnie balansowane lecz tylko mniej-wi cej. Na tyle aby byªy kusz c alternatyw dla AVL. 7.2 Rotacje Podobnie jak w drzewie AVL równie» tutaj wyst puj rotacje. Jednak»e wst puj tylko dwa rodzaje. W lewo oraz w prawo. 2 Ró»ne ¹ródªa podaj ró»ne warunki, s one jednak sobie równowa»ne 11
7.3 Dodawanie elementów Rysunek 2: Rotacje red-black Dodawanie jest operacj reukurencyjn realizowan nast puj co: 12
Listing 9: Wstawianie elementu do drzewa czerwono-czarnego I n s e r t (Node h, T val ) : j e ± l i h = n u l l : zwró new Node( val ) j e ± l i val mniejsze od h. val : I n s e r t (h >l e f t, val ) j e ± l i val wi ksze od h. val : I n s e r t (h >right, val ) j e ± l i val równe h. val : zako«cz j e ± l i h. r i g h t j e s t czerwony oraz lewy j e s t czarny : h = obró w lewo h j e ± l i lewy czerwony i lewy >lewy czerwony : h = obró w prawo j e ± l i lewy czerwony oraz prawy czerwony : zamie«kolory h oraz jego potomków Operacja realizowana rekurencyjnie charakteryzuje si niezwykª prostot implementacji, znacznie ªatwiejsz ni» analogiczna dla AVL. Istotne jest aby pamieta»e li±cie s reprezentowane przez wartos null przez co mo»emy spokojnie wywoªywa sprawdzanie czerwono±ci null-a. Po raz kolejny otrzymujemy zªo»ono± rz du O(log n). 13
7.4 Wyszukiwanie elementu Wyszukiwanie elementu jest operacj trywialn. Nale»y pami ta»e warunki drzewa BST s niezmiennie speªnione przez co mo»emy swobodnie zignorowa czerwono-czarn otoczk. Listing 10: Wyszukiwanie elementu w drzewie czerwono-czarnym Find (Node h, T val ) : j e ± l i h == n u l l zwró " nie znaleziono " j e ± l i val < h. val : Find (h. l e f t, val ) j e ± l i val > h. val : Find (h. right, val ) zwró " Znaleziono " Funkcj t mo»na równie» bez problemu zapisa iteracyjnie, ale wersja rekurencyjna urzeka swoj prostot. Ponadto drzewa s balansowane i jego wysko± jest nie wieksza ni» 2 lg n a ±rednio osi ga lg 2 przez co nie trzeba martwi si bª dem stack overow. Równie» tutaj otrzymujemy kalsyczne dla drzew O(log n). Czas wyszukiwania wydaje si wr cz absurdalnie krótki. 14
7.5 Wnioski Drzewa czerwono-czarne okazaªy si bardzo miª odmian drzew, która szczególnie spodobaªa si autorom. S wzgl dnie proste do zaimplementowania i daj doskonaªe czasy wykonania programów. Znalaªy one szerokie zastosowanie, gªównie ze wzgl du na doskonaª pesymistyczn zªo»ono± obliczeniow (O(log n). S u»ywanie w kontenerach bibliotek standardowych wielu jezyków, mechani¹mieszeregowania zada«linuksa, geometrii obliczneiowej. Zapewniaj dobry balans mi dzy czasem dodawania i wyszukiwania elementów (balansuj sie na tyle dobrze aby wyszukiwanie byªo szybkie, ale jednocze±nie na tyle szybko aby nie spowalnia wstawiania elementów). 15