Wykład X Wydział Matematyki Stosowanej Politechniki Śląskiej Gliwice, 2016 c Copyright 2016 Janusz Słupik
Drzewa binarne
Drzewa binarne Drzewo binarne - to drzewo (graf spójny bez cykli) z korzeniem (wyróżnionym wierzchołkiem), w którym każdy wierzchołek rodzic ma co najwyżej dwóch potomków, tzn. korzeń jest stopnia co najwyżej 2, a wszystkie inne wierzchołki są stopnia co najwyżej 3. Pojęcia: - poziomy drzewa, - liście (wierzchołki końcowe), - wysokość drzewa (tj. długość najdłuższej ścieżki od korzenia do liścia), - poddrzewo.
Wyszukiwanie Drzewa binarne są sposobem zorganizowania zbioru danych tak, aby ułatwić wyszukiwanie elementów tego zbioru (ang. BST - binary search tree). W zbiorze danych musi być zadany porządek. Np.: w zbiorze liczb relacja, w zbiorze słów porządek leksykograficzny. Niech BST ma wysokość h. Algorytm wyszukiwania musi wykonać pesymistycznie h porównań, aby znaleźć element, albo wykluczyć przynależność elementu do zbioru.
Drzewo zrównoważone Drzewo jest zrównoważone gdy różnica wysokości lewego i prawego poddrzewa każdego z wierzchołków wynosi co najwyżej jeden. Drzewo jest doskonale zrównoważone, gdy dodatkowo wszystkie liście znajdują się na co najwyżej dwóch poziomach. Algorytm DSW - równoważy BST poprzez szereg rotacji.
Reprezentacja drzewa binarnego Drzewa binarne są równoważne łańcuchom binarnym, które: - mają o jeden więcej bitów 0 niż bitów 1, - dla dowolnej pozycji k liczba bitów 0 występujących na lewo od tej pozycji jest nie większa niż liczba bitów 1 występujących na lewo od tej pozycji.
Własności omawianej reprezentacji Drzewo binarne jest zerem lub konkatenacją dwóch łańcuchów reprezentujących poddrzewa poprzedzonych jedynką. Ilość jedynek to ilość wierzchołków drzewa. Długość łańcucha reprezentującego drzewo to: 2*ilość wierzchołków + 1
Implementacja słownika z częstością słów oparta na drzewie binarnym
#include <stdio.h> #include <stdlib.h> #include <string.h> struct wierzcholek char *slowo; int ilosc; struct wierzcholek *lewy; struct wierzcholek *prawy; }; typedef struct wierzcholek* Wierzcholek;
Główną częścią naszej implementacji będzie funkcja o prototypie: Wierzcholek dodajslowo( Wierzcholek k, char *w ); której zadaniem będzie dodanie słowa w do poddrzewa o korzeniu k. Funkcja ta zwróci adres korzenia tego poddrzewa, tzn. jeśli k istniał to funkcja zwróci k, natomiast jeśli k ma wartość NULL, to funkcja zwróci adres nowo powstałego wierzchołka.
Wierzcholek dodajslowo( Wierzcholek k, char *w ) int wynik; if( k == NULL ) /* w to nowe slowo */ k = malloc( sizeof( struct wierzcholek ) ); if( k == NULL ) return NULL; k->slowo = malloc( strlen( w ) + 1 ); if( k->slowo!= NULL ) strcpy( k->slowo, w ); k->ilosc = 1; k->lewy = NULL; k->prawy = NULL; return k; } wynik = strcmp( k->slowo, w ); if( wynik == 0 ) k->ilosc++; else if( wynik < 0 ) k->lewy = dodajslowo( k->lewy, w ); else k->prawy = dodajslowo( k->prawy, w ); return k; }
void wypisz( Wierzcholek k ) if( k!= NULL ) wypisz( k->lewy ); printf("%s %d\n", k->slowo, k->ilosc ); wypisz( k->prawy ); } } void zwolnij( Wierzcholek k ) if( k!= NULL ) zwolnij( k->lewy ); zwolnij( k->prawy ); free( k->slowo ); free( k ); } }
main() Wierzcholek korzen = NULL; char slowo[20]; while(1) fgets( slowo, 20, stdin ); slowo[ strlen(slowo)-1 ] = \0 ; if( strcmp( slowo, "STOP" ) == 0 ) break; korzen = dodajslowo( korzen, slowo ); } wypisz( korzen ); zwolnij( korzen ); } system("pause"); return 0;
Zadanie Uzupełnij powyższą implementację BST o definicję funkcji o prototypie: int szukaj( Wierzcholek k, char *w ); która w poddrzewie o korzeniu k będzie wyszukiwała słowa w. Funkcja ta zwróci częstość zapisaną w wierzchołku odpowiadającym słowu w, lub 0 gdy takiego słowa nie ma w drzewie.
Wyszukiwanie z wykorzystaniem funkcji mieszającej Funkcja mieszająca (hash function) - jest to funkcja przyporządkowująca danym (ustalonego rodzaju) liczbę. Liczba ta jest interpretowana jako adres lub indeks. f : egzemplarz danych liczba Oczekiwane własności: - musi być łatwa do obliczenia, - ma w miarę równomiernie pokrywać przestrzeń adresów, - brak kolizji wartości (lub minimalna liczba kolizji), f (dane 1 ) = f (dane 2 ).
Koniec