Algorytmy i struktury danych Wykład 5: Drzewa Dr inż. Paweł Kasprowski pawel@kasprowski.pl Drzewa Struktury przechowywania danych podobne do list ale z innymi zasadami wskazywania następników Szczególny rodzaj grafów Łączą zalety tablic i list Szybkie wyszukiwanie Szybkie wstawianie i usuwanie Jest wiele rodzajów drzew Drzewa binarne Drzewa czewono-czarne B-drzewa Terminologia Elementy drzewa - węzły Każdy węzeł może mieć potomków (węzły na które wskazuje) Każdy węzeł oprócz korzenia ma swojego rodzica (węzeł, który na niego wskazuje) Korzeń pierwszy (górny) węzeł drzewa Liście węzły nie posiadające potomków (na samym dole drzewa) Drzewa binarne - każdy węzeł może mieć najwyżej dwóch potomków 1
Budowa drzewa binarnego Element pierwszy korzeń Po lewej elementy mniejsze Po prawej elementy większe Główny problem to zrównoważenie drzewa Drzewo skrajnie niezrównoważone staje się listą Wstawianie elementu public void wstaw(int klucz) { Element nowy = new Element(klucz); if(root==null) {root=nowy;return; boolean koniec = false; Element x = root; while(!koniec) { // szukanie miejsca do wstawienia elementu Wstawianie elementu public void wstaw(int klucz) { Element nowy = new Element(klucz); if(root==null) {root=nowy;return; boolean koniec = false; Element x = root; while(!koniec) { if(klucz<x.dana) { // w lewo if(x.lewy==null) {x.lewy = nowy;koniec=true; else x = x.lewy; if(klucz>x.dana) { // w prawo if(x.prawy==null) {x.prawy = nowy;koniec=true; else x = x.prawy; 2
Szukanie elementu public Element szukaj(int klucz) { Element x = root; while(x.dana!=klucz) { if(klucz>x.dana) x = x.prawy; else x = x.lewy; if(x==null) return null; return x; Znajdowanie minimum public Element min() { Element x = root; while(x.lewy!=null) x = x.lewy; return x; Znajdowanie maksimum public Element max() { Element x = root; while(x.prawy!=null) x = x.prawy; return x; 3
Wypisanie posortowanych danych public void wypisz() { wypisz(root); private void wypisz(element x) { if(x!= null) { wypisz(x.lewy); System.out.print(x.dana + ", "); wypisz(x.prawy); Złożoność operacji Dla drzewa zrównoważonego około połowa węzłów to liście (ostatni poziom) Złożoność wstawiania elementów rzędu O(log 2 N) Złożoność wyszukiwania elementów rzędu O(log 2 N) Dla drzew nie zrównoważonych w skrajnym przypadku tak jak dla listy Porównanie struktur Dla 1.000.000 elementów Tablica uporz. Tablica nieuporz. Lista Drzewo binarne Przesunięć przy wstawianiu Porównań przy szukaniu 500.000 1 1 20 20 500.000 500.000 20 4
Usuwanie węzła Najbardziej złożona operacja Kilka przypadków: usuwanie liścia (węzła bez potomków) usuwanie węzła z jednym potomkiem usuwanie węzła z dwoma potomkami Usuwanie węzła public boolean usun(int klucz) { Element x = root; Element rodzic = root; boolean prawedziecko = true; while(x.dana!=klucz) { rodzic = x; if(klucz>x.dana) { prawedziecko=true; x = x.prawy; else { prawedziecko=false; x = x.lewy if(x==null) return false; // tutaj x - węzeł do usunięcia, rodzic jego rodzic, // prawedziecko informacja czy x to dziecko prawe czy lewe rodzica... Usuwanie liścia... if(x.lewy=null && x.prawy==null) { if(x==root) root=null; if(prawedziecko) rodzic.prawy = null; else rodzic.lewy = null;... 5
Usuwanie węzła z prawym potomkiem... if(x.lewy == null) { if(x == root) root = x.prawy; else if(prawedziecko) rodzic.prawy = x.prawy; else rodzic.lewy = x.prawy;... Usuwanie węzła z lewym potomkiem... if(x.prawy == null) { if(x == root) root = x.lewy; else if(prawedziecko) rodzic.prawy = x.lewy; else rodzic.lewy = x.lewy;... Gdy jest dwóch potomków Element usuwany zastępuje się następnikiem Następnik następny co do wartości element w drzewie po usuwanym Jak go znaleźć? to minimalny element prawego podrzewa elementu usuwanego 6
Szukanie następnika public Element znajdznastępnik(element wezel) { Element x = wezel.prawy; while(x.lewy!= null) x = x.lewy; return x; Wstawienie następnika w miejsce usuwanego węzła Jeśli jest jego prawym potomkiem - przesunięcie całego podrzewa rodzic.{prawy_lub_lewy = nastepnik nastepnik.lewy = x.lewy Jeśli jest "głębiej" ł jest jednym z lewych potomków jego prawego potomka usuń następnik z drzewa rodzicnastepnika.lewy = nastepnik.prawy wstaw w miejsce usuwanego węzła rodzic.{prawy_lub_lewy = nastepnik nastepnik.prawy = x.prawy nastepnik.lewy = x.lewy Równoważenie drzewa Drzewo niezrównoważone traci swoje pozytywne właściwości Równoważenie drzewa jest kosztowne wymaga modyfikacji operacji wstawiania i usuwania Dlatego najczęściej dokonuje się "częściowego równoważenia" drzewa czerwono-czarne B-drzewa (balanced trees) 7
Drzewa binarne Struktura łącząca zalety listy (szybkie dodawanie elementów) tablicy uporządkowanej (szybkie wyszukiwanie) Problem: skomplikowane usuwanie elementów tylko drzewa zrównoważone mają takie zalety Zawsze jako liście Kolejność wstawiania wpływa na zrównoważenie drzewa Drzewo niezrównoważone w skrajnym przypadku staje się listą Tak więc problem jak zrównoważyć drzewo Usuwanie elementu Liść po prostu usunięcie Węzeł z jednym potomkiem potomek wstawiany w miejsce węzła Węzeł z dwoma potomkami wyszukanie następnika "wyciągnięcie" następnika z drzewa (ma on max 1 potomka) wstawienie następnika w miejsce węzła 8
Przykład wykorzystania drzewa Jak przesłać zwykły tekst za pomocą zer i jedynek Każdą literę należy zakodować kod ASCII każda litera ma swój 7-bitowy kod (lub 8-bitowy!) Jak to zrobić szybciej? Problem jest bardzo stary z czasów dalekopisu i alfabetu Morse'a Zasada litery występujące częściej mają krótsze kody (np. 'e' kropka, 'i' dwie kropki, 'h' cztery kreski) Kod Huffmana Problem z alfabetem Morse'a oprócz liter (kresek i kropek) trzeba także przesyłać znaki przedzielające litery Kod Huffmana Kod bez konieczności wstawiania znaków końca litery Najkrótsze są znaki najczęściej używane Przykład: zdanie: SUSIE SAYS IT IS EASY występowanie znaków: s(6), _(4), i(3), a,e,y(2), t, u(1) Kody znaków dla przykładu Znak Ilość wystąpień Kod A 2 010 E 2 1111 I 3 110 S 6 10 T 1 0110 U 1 01111 Y 2 1110 _ 4 00 <LF> 1 01110 Napis zakodowany: 65 bitów 9
Drzewo 0 22 1 9 13 4 5 6 7 _ 2 3 S 3 4 A 1 2 I 2 2 T 1 LF 1 U Y E Tworzenie drzewa Każda litera węzeł Łączenie dwóch najmniej częstych w drzewo 2 1 LF 1 U Kolejne łączenia dwóch najmniej licznych drzew aż do uzyskania jednego drzewa Zysk: 65 bitów zamiast 88 Równoważenie drzew Najprostsza metoda: wykonanie obrotu Zmiana rodzica z jednym z potomków (prawym lub lewym) Możliwy jest obrót całych poddrzew Niestety operacje obrotu są kosztowne Problem: kiedy dokonać obrotu i jaki obrót będzie najefektywniejszy? 10
Drzewa wielokierunkowe Każdy węzeł może mieć wielu (N) potomków Każdy węzeł może przechowywać więcej niż jeden element (maksymalnie N-1) Przy wstawianiu i i usuwaniu elementów pilnujemy aby każdy węzeł był odpowiednio wypełniony Najczęściej stosowana wersja drzew wielokierunkowych to B-drzewa (B-tree balanced tree) Drzewo wielokierunkowe 3 rzędu 20 80 1 11 31 42 81 90 Drzewo wielokierunkowe 4 rzędu 10 20 42 1 4 7 11 17 19 21 25 33 45 55 70 11
Drzewo wielokierunkowe 5 rzędu 20 40 60 80 2 4 10 18 41 44 50 56 88 90 91 99 21 24 33 38 62 63 69 77 Procedura wstawiania elementów Szukaj liścia do którego wstawić element Jeśli liść niepełny wstaw element i zakończ Jeśli liść pełny przenieś element środkowy y do węzła-rodzica Jeśli rodzic pełny - przenieś element środkowy do węzła-dziadka itd. 1 12
2 1 3 1 2 Brak miejsca w węźle ę podział na dwie części, ę środek poziom wyżej 4 2 1 3 13
5 2 1 3 4 Brak miejsca w węźle podział na dwie części, środek poziom wyżej 6 2 4 1 3 5 7 2 4 1 3 5 6 Brak miejsca w węźle podział na dwie części, środek poziom wyżej 14
2 4 6 1 3 5 7 Brak miejsca w węźle podział na dwie części, środek poziom wyżej 4 2 6 1 3 5 7 Pomimo wstawiania liczb posortowanych drzewo ciągle zrównoważone! Wyszukiwanie w dużych bazach Nie wszystko mieści się w pamięci Dane przechowywane są na dysku twardym Dostęp do dysku jest wolny ęp y j y Liczba odczytów i zapisów staje się kluczowa Odczyt i zapis zawsze dotyczy całego bloku danych 15
Działanie dysku twardego Wyszukaj ścieżkę, gdzie zapisane są dane Ustaw nad nim głowicę Poczekaj aż właściwy sektor znajdzie się pod głowicą Odczytaj blok danych Wniosek Czas wyszukiwania danej jest o wiele większy od czasu odczytu Należy minimalizować liczbę odczytów daleko od siebie Przykład dużej bazy Baza zawierająca dane mieszkańców Katowic 500 tys. rekordów, każdy 1024 bajty Razem 512 MB nie zawsze można to zmieścić w pamięci Wszelkie zmiany muszą zostać zapisane na dysku pamięć jest ulotna! Dane zapisywane są na dysku w blokach (stronach) wielkości 16 kb 16 rekordów na blok W sumie 31.250 bloków Wyszukanie danych Wyszukiwanie danych nieposortowanych to średnio 15.625 odczytów (5 minut dla 10ms czasu dostępu) Wyszukiwanie danych posortowanych (wyszukiwanie binarne) to około 15odczytów(log 2 N) Dwa problemy: Wstawianie danych do tablicy posortowanej to średnio 15.625 odczytów a następnie zapisów dla 10 ms czasu dostępu to 5 minut oczekiwania Sortowanie przyspiesza wyszukiwanie tylko według jednego pola (np. nazwiska). Gdy szukamy np. wg adresu wracamy do wyszukiwania nieposortowanego 16
Indeksy oparte na B-drzewach Każdy węzeł ma wielkość strony Elementy w węźle składają się z: wartości elementu (np. nazwisko) adresu strony na której można znaleźć cały rekord Jeden element: 20B (nazwisko) + 4B (adres strony) 1 strona/węzeł - 16kB/24B = 682 elementy Potrzebne 733 strony po 16kB Całkowita wielkość indeksu ok. 12 MB Cały mieści się w pamięci! Indeksy - podsumowanie Dla indeksu mieszczącego się w całości w pamięci Wyszukiwanie Liczba odczytów z dysku: jeden Dopisywanie Liczba zapisów przy zmianach: jeden Dla indeksu nie mieszczącego się w pamięci Jeden odczyt daje informacje o położeniu 682 elementów Możliwość założenia wielu indeksów na różne pola Wada indeksów: konieczność aktualizacji przy zmianach Egzamin AiSD Na zaliczenie: Znajomość podstawowych algorytmów dotyczących tablic i list i umiejętność ich modyfikacji Znajomość zasady działania rekurencji i umiejętność tworzenia prostych algorytmów rekurencyjnych Znajomość sposobu działania (rozumienie) algorytmów i struktur omówionych na zajęciach Na ocenę wyższą: Umiejętność tworzenia bardziej złożonych algorytmów operujących na tablicach i listach nie opartych bezpośrednio na algorytmach omówionych na zajęciach Umiejętność tworzenia procedur rekurencyjnych do rozwiązywania postawionych problemów Umiejętność tworzenia procedur operujących na drzewach 17
Dziękuję za uwagę Do zobaczenia... materiały dostępne pod adresem: www.kasprowski.pl 18