Programování v C++ 1, 16. cvičení binární vyhledávací strom 1 1 Fakulta jaderná a fyzikálně inženýrská České vysoké učení technické v Praze Zimní semestr 2018/2019
Přehled 1 2
Shrnutí minule procvičené látky Šablona spojového seznamu šablony tříd organizace programu šablona metod deklarace friend pro šablonu konstruktory a destruktor přidávání a odebírání prvků hledání prvků mazání prvků některé přetížené operátory operátor přiřazení operátor spojování seznamů operátor indexování (pozor na složitost!)
N-ární strom typu T Definice N-ární strom typu T je prázdná struktura nebo je to prvek typu T, ke kterému je připojeno nejvýše N disjunktních stromů typů N (podstromy). rekurzivní definice, rekurzivní datová struktura N = 2 binární strom Binární vyhledávací strom Binární vyhledávací strom je strom, pro jehož každý vrchol V platí následující pravidla: 1 data uložená v levém podstromu V jsou menší než data uložená ve V, 2 dala uložená v pravém podstromu V jsou větší než data uložená ve V.
Zpracování stromu přirozeně rekurzivní přímé zpracování (preorder) 1 zpracuj data v kořenu 2 zpracuj data v levém podstromu 3 zpracuj data v pravém podstromu vniřní zpracování (inorder) 1 zpracuj data v levém podstromu 2 zpracuj data v kořenu 3 zpracuj data v pravém podstromu zpětné zpracování (postorder) 1 zpracuj data v levém podstromu 2 zpracuj data v pravém podstromu 3 zpracuj data v kořenu
Zadání příkladu Úloha Vytvořte šablonu Tree<T>, která bude reprezentovat binární vyhledávací strom. Implementujte následující operace: přidání nového vrcholu tisk dat uložených ve stromu zrušení stromu vyhledání vrcholu s danými daty odstranění vrcholu s danými daty
Přehled 1 2
Vrchol stromu šablona Node<T> 1 template <typename T> 2 class Node{ 3 T data; 4 Node<T> *left, *right; 5 public: 6 Node(T d); 7 T getdata(); 8 void setdata(t d); 9 template<typename X> friend class Tree; 10 }; 11 12 template<class T> 13 Node<T>::Node(T d) 14 :data(d), left(0), right(0){} 15 16 template<class T> 17 void Node<T>::setData(T d){ data = d; } 18 19 template<class T> 20 T Node<T>::getData(){ return data; }
Vložení vrcholu do podstromu jestliže je podstrom prázdny, udělej z nového prvku jeho kořen jestliže je podstrom neprázdný, tak nová data zařad do jeho levého nebo pravého podstromu podle pravidla uspořádání 1 template<class T> //sablona soukrome metody 2 void Tree<T>::insertIntoTree(Node<T> *&r, T d){ 3 if(!r) r = new Node<T>(d); 4 else if(r->getdata() > d) insertintotree(r->left, d); 5 else insertintotree(r->right, d); 6 } pro vložení do celého stromu: 1 template<class T> //sablona verejne metody 2 void Tree<T>::insertData(T d){ 3 insertintotree(root, d); 4 }
Tisk dat uložených ve vrcholech stromu vnitřní uspořádání vytiskne data podle velikosti potřeba testovat, že strom je neprázdný (jinak dereference nulového ukazatele) 1 template<class T> 2 void Tree<T>::print(){ 3 printree(root); 4 } 5 6 template<class T> 7 void Tree<T>::prinTree(Node<T>* r){ 8 if(r){ 9 printree(r->left); 10 std::cout << r->data << std::endl; 11 printree(r->right); 12 } 13 }
Zrušení stromu úloha pro destruktor potřeba použít zpětné zpracování 1 template<class T> 2 Tree<T>::~Tree(){ 3 cleantree(root); 4 } 5 6 template<class T> 7 void Tree<T>::cleanTree(Node<T> *&r){ 8 if(r){ 9 cleantree(r->left); 10 cleantree(r->right); 11 delete r; 12 r = nullptr; 13 } 14 }
Vyhledání vrcholu se zadanými daty pokud data nejsou v kořeni, použít pravidlo uspořádání a rekurzivně prohledat levý nebo pravý podstrom pamatovat si ukazatel na předchůdce (potřeba při mazání) 1 template<class T> 2 Node<T>* Tree<T>::findData(T d){ 3 Node<T> *prev; 4 return findnode(root, prev, d); 5 } 6 7 template<class T> 8 Node<T>* Tree<T>::findNode(Node<T> *r, Node<T> *&prev, T d){ 9 if(!r) return 0; //v prazdnem podstromu prvek nelezi 10 if(r->data == d) return r; //prvek nalezen 11 prev = r; //jdeme do podstromu, oznacime predchudce 12 if(r->data > d) return findnode(r->left, prev, d); 13 else return findnode(r->right, prev, d); 14 }
Přehled 1 2
Mazání listu mazání listu 1 v předchůdci listu odstraň ukazatel na mazaný list 2 smaž zadaný list mazání vrcholu s jedním následníkem mazání vrcholu se dvěma následníky 1 template<class T> 2 void Tree<T>::removeInLeaf(Node<T> *&node, Node<T> *pred){ 3 if(!pred) root = 0; //maze se koren 4 else{ 5 if(pred->left == node) pred->left = 0; 6 else pred->right = 0; 7 } 8 delete node; 9 node = 0; 10 }
s jedním následníkem analogie mazání ve spojovém seznamu 1 získej adresu následníka mazaného prvku 2 adresu následníka ulož do odpovídajícího předchůdce mazaného prvku 3 smaž zadaný prvek 1 template<class T> 2 void Tree<T>::removeInBranch(Node<T> *&node, Node<T> *pred){ 3 Node<T> *q; 4 if(node->left) q = node->left; 5 else q = node->right; 6 if(!pred) root = q; 7 else{ 8 if(pred->left == node) pred->left = q; 9 else pred->right = q; 10 } 11 delete node; 12 node = 0; 13 }
se dvěma následníky převedení na předchozí případy 1 template<class T> void Tree<T>::removeData(T d){ 2 Node<T> *pred = 0; 3 Node<T> *node = findnode(root, pred, d); 4 if(!node) return; 5 if((!node->left) && (!node->right)) removeleaf(node, pred); 6 else if((!node->left) (!node->right)) removeinbranch(node,pred); 7 else{ 8 //najdi nejpravejsi vrchol leveho podstromu 9 pred = node; //aktualizace ukazatele na predchudce 10 Node<T> *q = node->left; 11 while(q->right){ 12 pred = q; 13 q = q->right; 14 } 15 node->data = q->data; //prohozeni dat 16 if(!q->left) removeinbranch(q, pred); 17 else removeleaf(q, pred); 18 } 19 } Odebrání vrcholu se zadanými daty
Shrnutí Binární strom strom typu T pravidlo uspořádání vrcholů ve stromu zpracování stromu přímé zpracování vnitřní zpracování pozdní zpracování základní operace se stromem: vložení vrcholu do stromu tisk dat uložených ve vrcholech zrušení stromu vyhledání vrcholu odstranění vrcholu