Definicja liczb Fibonacciego:

Transkrypt

Definicja liczb Fibonacciego:
Wieczorowe Studia Licencjackie
Wrocław, 9.01.2007
Wstęp do programowania
Wykład nr 14
Drzewa binarnych poszukiwań (BST)
Definicja struktur danych:
typedef struct node *pnode;
typedef struct node{
int key;
pnode left; pnode right;} snode;
Własność drzewa BST:
Dla każdego węzła drzewa v:
• Klucze znajdujące się w jego lewym poddrzewie są mniejsze (lub równe) od klucza w węźle v,
• Klucze znajdujące się w jego prawym poddrzewie są większe (lub równe) od klucza w węźle v.
Przykłady:
to nie jest drzewo BST
5
4
3
8
7
9
Poniżej poprawne drzewo BST
5
3
1
8
4
9
Podstawowe funkcje
Wyszukiwanie klucza:
pnode search( pnode root, int skey)
{
if (root==NULL || root->key == skey) return root;
if (root->key > skey)
return search(root->left, skey);
else return search(root->right, skey);
}
Wstawianie nowego elementu:
pnode insert( pnode root, int nkey)
//jeśli w drzewie BST na które wskazuje root
//nie ma elementu o wartosci klucza rownej nkey,
//taki element jest wstawiany
{
pnode pom;
if (root==NULL){ // tworzenie nowej struktury
pom = (pnode) malloc(sizeof(snode));
pom->left = pom->right = NULL;
pom->key = nkey;
return pom;
}
if (nkey < root->key)
root->left = insert(root->left, nkey);
else
if (nkey > root->key)
root->right = insert(root->right, nkey);
return root;
}
Wypisanie elementów w kolejności rosnącej (porządek inorder).
void write( pnode root )
{
if (root!=NULL){
write( root->left); // wypisz mniejsze
printf("%d\n", root->key); // wypisz dany element
write( root->right); //wypisz wieksze
}
}
Usuwanie elementu:
pnode delhlp(pnode root, pnode akt) //FUNKCJA POMOCNICZA!
{
pnode pom;
//
//
//
if
}
najmniejszy element prawego poddrzewa wezla root
usuwamy, a jego klucz umieszczamy w wezle root
UWAGA: element najmniejszy NA PEWNO nie ma lewego poddrzewa
(akt->left!=NULL) { // akt nie jest najmniejszy
akt->left = delhlp(root, akt->left);
return akt;
} else // akt wskazuje na najmniejszy element w poddrzewie root
{
root->key = akt->key;
pom=akt->right;
free(akt);
return pom;
}
pnode deletekey( pnode root, int dkey)
{ // usuwa element o kluczu dkey z drzewa, na które wskazuje root
pnode pom;
if (root!=NULL)
if (dkey < root->key) // szukany element w lewym poddrzewie
root->left = deletekey(root->left, dkey);
else
if (dkey > root->key) // szukany element w prawym poddrzewie
root->right = deletekey(root->right, dkey);
else // root wskazuje na element do usuniecia
if (root->left == NULL){ // brak lewego poddrzewa
pom=root->right;
// "podczepiamy" prawe poddrzewo
free(root);
root=pom;
} else
if (root->right == NULL){ //brak prawego poddrzewa
pom=root->left;
//"podczepiamy" lewe poddrzewo
free(root);
root = pom;
} else
delhlp(root, root->right); // ma oba poddrzewa
return root;
}
Drzewa BST bez rekurencji
pnode search( pnode root, int skey)
{
while (root!=NULL && root->key != skey)
if (root->key > skey) root = root->left;
else root = root->right;
return root;
}
pnode insert( pnode root, int nkey)
/* jeśli w drzewie BST na które wskazuje root
nie ma elementu o wartosci klucza rownej nkey,
taki element jest wstawiany*/
{
pnode pom, prev, hlproot;
}
prev = hlproot = root;
while (root!=NULL && root->key != nkey)
{ prev = root;
if (root->key > nkey) root = root->left;
else root = root->right;
}
if (root==NULL){ /* tworzenie nowej struktury*/
pom = (pnode) malloc(sizeof(snode));
pom->left = pom->right = NULL;
pom->key = nkey;
if (prev==NULL) return pom;
else
if (nkey >= prev->key) prev->right = pom;
else prev->left = pom;
}
return hlproot;
Zastosowanie wartownika w drzewach BST i listach
Definicja struktur danych:
typedef struct node *pnode;
typedef struct node{
int key;
pnode left; pnode right;} snode;
Podobnie do sekwencyjnego przeszukiwania tablic, przy przeszukiwaniu listy bądź drzewa BST w celu
znalezienia jakiegoś elementu, wykonujemy pętlę (lub rekurencję) o dwóch warunkach zakończenia:
-
doszliśmy do końca przeszukiwanego zbioru elementów (koniec zakresu danych w tablicy, koniec listy,
brak odpowiedniego potomka w drzewie),
doszliśmy do poszukiwanego elementu.
Z uwagi na fakt, że sprawdzenie obu warunków jest konieczne przy każdym kolejnym elemencie zbioru, jest ono
kluczowe dla czasu działania wyszukiwania.
Przy przeszukiwaniu tablic stosowaliśmy technikę pozwalającą zoptymalizować przeszukiwanie, nazywaną
szukaniem z wartownikiem. Polega ona na:
- umieszczeniu „fikcyjnego” elementu na końcu ciągu,
- wstawianiu szukanego elementu na pozycję tego elementu fikcyjnego.
Dzięki temu:
- nie musieliśmy sprawdzać czy doszliśmy do końca ciągu, ponieważ wartownik gwarantował dotarcie
do poszukiwanego elementu.
Ogólny schemat przeszukiwania z wartownikiem jest następujący:
-
utwórz fikcyjny element na końcu ciągu
nadaj temu fikcyjnemu elementowi wartość x równą elementowi poszukiwanemu,
przeszukaj cały ciąg w poszukiwaniu x, z jednym warunkiem zakończenia: aktualny element jest równy
x.
po zakończeniu, sprawdź czy zatrzymałeś się na elemencie fikcyjnym:
o jeśli tak, x nie występuje w oryginalnym ciągu,
o w przeciwnym razie x występuje w ciągu oryginalnym.
Poniżej poprawne drzewo BST
5
3
1
NULL
NULL
8
4
NULL
NULL
NULL
NULL
9
NULL
Przypomnijmy rekurencyjną funkcję wyszukiwania klucza:
pnode search( pnode root, int skey)
{
if (root==NULL || root->key == skey) return root;
if (root->key > skey)
return search(root->left, skey);
else return search(root->right, skey);
}
oraz iteracyjną...
pnode search( pnode root, int skey)
{
while (root!=NULL && root->key != skey)
if (root->key > skey) root = root->left;
else
root = root->right;
return root;
}
W obu wariantach konieczne jest sprawdzenie 2 warunków przy przejściu każdego węzła. Na koniec, mamy:
- jeśli search(root, skey) jest równe NULL, to skey nie występuje w drzewie o korzeniu
root;
- w przeciwnym razie skey występuje w drzewie.
Idea użycia wartownika polega tutaj na stworzeniu fikcyjnego węzła wartownik, „zamiast” wartości NULL.
Następnie:
- Każdy wskaźnik pusty (NULL) zastępujemy wskaźnikiem na wartownika;
- Przy poszukiwaniu elementu x, wartość x umieszczamy w węźle wartownik.
Poniżej ilustracja tej metody na drzewie z poprzedniego przykładu:
5
3
1
8
4
9
x
wartownik
A teraz wyszukiwanie z wartownikiem (iteracyjnie):
pnode search( pnode root, pnode wartownik, int skey)
{
wartownik->key = skey;
while (root->key != skey)
if (root->key > skey) root = root->left;
else
root = root->right;
return root;
}
Na koniec,
- jeśli search(root, wartownik, skey) jest równe wartownik, to skey nie występuje w
drzewie o korzeniu root;
- w przeciwnym razie skey występuje w drzewie.
Uwaga: zastosowanie wartownika wymaga również odpowiednich zmian w pozostałych funkcjach operujących
na drzewach BST:
- drzewo puste należy „zainicjować” poprzez utworzenie jednego węzła, odpowiadającego wartownikowi;
-
pnode init()
{ pnode hlp;
hlp = (pnode) malloc(sizeof(snode));
return hlp;
}
każde wstawienie elementu wykorzystuje poniżej opisane wyszukiwanie i tworzy odowiednie dowiązania
do wartownika;
pnode insert( pnode root, pnode wartownik, int nkey)
/* jeśli w drzewie BST na które wskazuje root
nie ma elementu o wartosci klucza rownej nkey,
taki element jest wstawiany*/
{
pnode pom, prev, hlproot;
wartownik->key = nkey;
prev = hlproot = root;
while (root->key != nkey)
{ prev = root;
if (root->key > nkey) root = root->left;
else root = root->right;
}
if (root==wartownik){ /* tworzenie nowej struktury*/
pom = (pnode) malloc(sizeof(snode));
pom->left = pom->right = wartownik;
pom->key = nkey;
if (prev==wartownik) return pom;//drzewo puste
else
if (nkey >= prev->key) prev->right = pom;
else prev->left = pom;
}
return hlproot;
}
-
wypisanie elementów w kolejności rosnącej (porządek inorder).
void write( pnode root, pnode wartownik )
{
if (root!=wartownik){
write( root->left); // wypisz mniejsze
printf("%d\n", root->key); // wypisz dany element
write( root->right); //wypisz wieksze
}
}
-
podobnie działa też usuwanie.

Podobne dokumenty