Drzewa BST

BST = Binary Search Tree = drzewo poszukiwań binarnych

Drzewo BST to drzewo binarne (niekoniecznie pełne), posiadające "własność drzewa BST".

Każdy wierzchołek "x" drzewa BST posiada (przynajmniej) następujące pola:

 
Własność drzewa BST: 
    niech "x" będzie dowolnym wierzchołkiem drzewa; 
    wszystkie wierzchołki lewego poddrzewa wierz "x" mają klucze < x.klucz 
    wszystkie wierzchołki prawego poddrzewa wierz "x" mają klucze > x.klucz
 
Przykład drzewa BST:

 
Założenie:
Dla uproszczenia zakładamy że wszystkie klucze w drzewie BST są różne !

Do czego służą drzewa BST ?

Jest wiele innych możliwych zastosowań. Okazuje się że podstawowe operacje na drzewach BST wymagają czasu równego wysokości drzewa. Problem polega na tym że nie zawsze można zagwarantować małą wysokość drzewa - zależy to od sposobu tworzenia drzewa - od kolejności w jakiej wstawiamy wierzchołki drzewa.
 

Uwagi implementacyjne:
Wierzchołki drzewa BST muszą być tworzone "dynamicznie", należy używać "wskazań".
Wierzchołek drzewa BST reprezentujemy przy pomocy struktury/rekordu BST_Wierz :

 

Oto podstawowe operacje na drzewie BST:

Zadanie 20

Zaimplementuj poniższe procedury, posiłkując się wskazówkami.
Odpowiedz także na pytania (dlaczego ?); można się posiłkować powyższym rysunkiem.
Zademonstruj działanie procedur na małym przykładzie.
void BST_WstawWierz(BST_Wierz *&korzen, int klucz);

void BST_WstawWierz(BST_Wierz *&korzen, BST_Wierz *wierz);
oczywiście nowy wierzchołek należy wstawiać tak aby nie popsuć własności drzewa BST;
nowy wierzchołek wstawiamy zawsze jako liść drzewa, należy tylko wyszukać odpowiednie miejsce: sprawdzamy czy "klucz" jest >= od "korzen->klucz" jeśli tak to wierzchołek zostanie wstawiony w prawym poddrzewie, w przeciwnym wypadku w lewym poddrzewie itd ...
.
void BST_PokazInorder(BST_Wierz *korzen);
przechodzić drzewo metodą "inorder", tj najpierw rekurencyjne wywołanie dla lewego poddrzewa, potem wypisanie klucza bieżącego wierchołka, potem rekurencyjne wywołanie dla prawego poddrzewa
void BST_PokazPreorder2(BST_Wierz *korzen);
przechodzić drzewo metodą "preorder", tj najpierw wypisanie klucza, potem rekurencyjne wywołanie dla lewego poddrzewa, potem rekurencyjne wywołanie dla prawego poddrzewa; dodatkowo dla każdego klucza podajemy klucz jego ojca; np "5() 2(5) 8(5) 7(8) 9(8)"
.
BST_Wierz *BST_Znajdz(BST_Wierz *korzen, int klucz);
oczywista rekurencja; jeśli bieżący wierzchołek nie ma poszukiwanego klucza to wiemy w którym poddrzewie (lewym czy prawym) może się poszukiwany klucz znajdować na podstawie "własności drzewa BST"
.
BST_Wierz *BST_Maksimum(BST_Wierz *korzen);
wystarczy "iść" po prawej gałęzi drzewa BST, w kierunku od korzenia do liści, tak długo jak to możliwe
.
BST_Wierz *BST_Nastepnik(BST_Wierz *wierz);
1) jeśli "wierz" posiada prawe poddrzewo, to wtedy zwracamy minimalny element w prawym poddrzewie (dlaczego ?)
2) w przeciwnym wypadku szukamy wierzchołka takiego, że największym kluczem jego lewego poddrzewa jest "wierz->klucz"; innymi słowy idziemy od wierzchołka "wierz" w górę drzewa tak długo aż napotkamy wierzchołek, który jest lewym synem swojego ojca; klucz ojca jest właśnie poszukiwanym następnikiem (na powyższym rysunku następnikiem 13 jest 15)
.