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.