6. Alberi binari NICOLETTA DE FRANCESCO • NULL è un albero binario ; • un nodo p più due alberi binari Bs e Bd forma un albero binario Algoritmi e strutture dati II parte p è radice Bs è il sottoalbero sinistro di p Bd il sottoalbero destro di p 2003/2004 alberi etichettati Algoritmi e strutture dati 1 Algoritmi e strutture dati 6. Alberi binari 2 6. Ricorsione su alberi binari A • padre • figlio sinistro (figlio destro) caso base albero vuoto (NULL) caso ricorsivo radice + due sottoalberi C B • antecedente • foglia D • discendente E F • livello di un nodo • livello dell'albero G H Algoritmi e strutture dati 3 Algoritmi e strutture dati 6. Visita anticipata (preorder) 6. Visita differita (postorder) preOrder ( albero ) { se l'albero binario non e' vuoto { esamina la radice; preOrder ( sottoalbero sinistro); preOrder ( sottoalbero destro ); } } postOrder ( albero ) { se l'albero binario non e' vuoto { postOrder ( sottoalbero sinistro); postOrder ( sottoalbero destro); esamina la radice; } } A B A C B A B D C E G H F D E G H E G 5 C DBGHEFCA D F Algoritmi e strutture dati 4 F H Algoritmi e strutture dati 6 6. Visita simmetrica (inorder) 6. Memorizzazione in lista multipla inOrder ( albero ) { se l'albero binario non e' vuoto { inOrder ( sottoalbero sinistro); esamina la radice; inOrder ( sottoalbero destro); } } struct Node { InfoType label; Node* left; Node* right; }; label left right A A B D B A G E H C F C A C B D E G C B F D H D Algoritmi e strutture dati 7 Algoritmi e strutture dati 6. visite in C++ void preOrder(Node* tree) { 6. Visite in C++ void preOrder(Node* tree) { if (tree) { void postOrder(Node* tree) { <esamina tree->label>; cout << tree->label; preOrder(tree->left); preOrder(tree->left); preOrder(tree->right); preOrder(tree->right); } } Algoritmi e strutture dati if (tree) { postOrder(tree->left); inOrder(tree->left); postOrder(tree->right); <esamina tree->label>; <esamina tree->label>; inOrder(tree-> right); } } } } void inOrder(Node* tree) { if (tree) { if (tree) { } 8 } 9 Algoritmi e strutture dati 6. Complessità delle visite 10 6. Alberi binari bilanciati ALBERO BINARIO BILANCIATO Complessità in funzione del numero di nodi: i nodi di tutti i livelli tranne quelli dell'ultimo hanno due figli T(0) = a T(n) = b+T(ns)+T(nd ) con ns+n d=n-1 n>0 Caso particolare : T(0) = a bilanciato non bilanciato T(n) = b+T(n/2)+T(n/2) Un albero binario bilanciato con livello k ha 2 (k+1) -1 nodi e 2k foglie T(n) ∈ ∈ O(n) Algoritmi e strutture dati 11 Algoritmi e strutture dati 12 6. Alberi binari 6. Complessità nel numero dei livelli ALBERO BINARIO QUASI BILANCIATO Complessità in funzione dei livelli (se l’albero è bilanciato): fino al penultimo livello è un albero bilanciato T(0) = a T(n) = b+2T(n-1) T(n) ∈ ∈ O(2n ) quasi bilanciato quasi bilanciato Algoritmi e strutture dati 13 Algoritmi e strutture dati 6. Alberi binari: conta i nodi e le foglie 14 6. Alberi binari: cerca un’etichetta conta i nodi int nodes (Node* tree) { if (!tree) return 0; // albero vuoto return 1+nodes(tree->left)+nodes(tree->right); } ritorna il puntatore al nodo che contiene l’etichetta n. Se l’etichetta non compare nell’albero ritorna NULL. Se più nodi contengono n, ritorna il primo nodo che si incontra facendo la visita anticipata Node* findNode (Infotype n, Node* tree) { conta le foglie int leaves (Node* tree) { if (!tree) return 0; // albero vuoto if ( !tree->left && !tree->right ) return 1; // foglia return leaves(tree->left)+leaves(tree->right); } // trovata :ritorna il puntatore Node* a=findNode (n, tree->left); if (a) return a; // cerca a sinistra // se trovata ritorna il puntatore else return findNode(n, tree->right); // cerca a destra } 15 6. Alberi binari: cancella tutto l’albero Algoritmi e strutture dati 16 6. Alberi binari: inserisci un nodo inserisce un nodo (son) come figlio di father, sinistro se c=‘l’, destro se c=‘r’. Ritorna 1 se l’operazione ha successo, 0 altrimenti. Se l’albero è vuoto, inserisce il nodo come radice void delTree(Node* &tree) { if (tree) { delTree(tree->left); delTree(tree->right); delete tree; tree=NULL; } } int insertNode (Node* tree, InfoType son, InfoType father, char c){ if (!tree) { // albero vuoto tree=new Node; tree ->label=son; tree ->left = tree ->right = NULL; return 1; } alla fine il puntatore deve essere NULL Algoritmi e strutture dati //albero vuoto: l’etichetta non c’è return tree; T(n) ∈ ∈ O(n) Algoritmi e strutture dati if (!tree) return NULL; if (tree->label==n) 17 Algoritmi e strutture dati 18 6. Alberi binari: inserisci un nodo (cont.) 6. Alberi binari: inserisci un nodo (cont.) Node* a=findNode(father,tree); //cerca father if (!a) return 0; //father non c’è if (c=='l' && !a->left ) { if (c=='r' && !a->right) { //inserisci come figlio destro a->right=new Node; //inserisci come figlio sinistro a->right->label=son; a->left=new Node; a->left->label=son; a->right->left = a->right->right = NULL; a->left->left =a->left->right=NULL; return 1; } return 1; return 0; } //inserimento impossibile } Algoritmi e strutture dati 19 Algoritmi e strutture dati 6. Class BinTree 20 6. Class BinTree public: template<class InfoType> BinTree() { root = NULL; }; class BinTree { struct Node { ~BinTree(){ delTree(root); }; InfoType label; int find(InfoType x) { return findNode(x, root); }; Node *left, *right; void pre() { preOrder(root); }; }; Node *root ; void post(){ postOrder(root); }; Node* findNode(InfoType, Node*); void in() { inOrder(root); }; void preOrder(Node*); void inOrder(Node*); int insert( InfoType son, InfoType father, char c) { insertNode(root,son, father,c); void postOrder(Node*); void delTree(Node*&); }; int insertNode(Node*, InfoType, InfoType, char) }; Algoritmi e strutture dati 21 Algoritmi e strutture dati 7.1 Alberi generici: definizione 7.1 Alberi generici: differenza con alberi binari alberi binari • un nodo p è un albero • un nodo + una sequenza di alberi A1 .. An è un albero A B • radice B sottoalbero sinistro vuoto A • i-esimo sottoalbero B C • i-esimo figlio • livello A diverso da sottoalbero destro vuoto • padre 22 D E R alberi generici A F unico albero : radice: A, un sottoalbero B G Algoritmi e strutture dati H 23 Algoritmi e strutture dati 24 7.1 Alberi generici: visite 7.1 Alberi generici: visite preOrder ( albero ) { postOrder ( albero ) { esamina la radice; se l'albero ha n sottoalberi { preOrder ( primo sottoalbero); … preOrder ( n-esimo sottoalbero); se l'albero ha n sottoalberi { postOrder ( primo sottoalbero); … postOrder ( n-esimo sottoalbero); esamina la radice ; } } A B A C D R E B ABDCEGHFR F D H G E G Algoritmi e strutture dati 25 7.1 Alberi generici: memorizzazione C R DBGHEFCRA F H Algoritmi e strutture dati 26 7.1 Alberi generici: corrispondenza fra visite MEMORIZZAZIONE FIGLIO-FRATELLO Utilizzando la memorizzazione figlio-fratello: • primo figlio a sinistra • primo fratello a destra la visita preorder del trasformato corrisponde alla visita preorder dell’albero generico Α Β A B D C E C D R F G G H la la visita inorder del trasformato corrisponde alla visita postorder dell’albero generico R Ε F H Algoritmi e strutture dati 27 7.2 Esempi di programmi su alberi generici: conta nodi e foglie Algoritmi e strutture dati 28 7.2 Esempi di programmi su alberi generici: inserimento conta i nodi (vedi albero binario) inserisce un nodo in fondo a una lista di fratelli int nodes (Node* tree) { if (!tree) return 0; return 1+nodes(tree->left)+nodes(tree->right); } conta le foglie int leaves(Node* tree) { if (!tree) return 0; if (!tree->left) return 1+ leaves(tree->right); // foglia return leaves(tree->left)+ leaves(tree->right); } Algoritmi e strutture dati 29 void addSon(InfoType x, Node* &tree) { if (!tree) { //lista vuota tree=new Node; tree->label=x; tree->left = tree->right = NULL; } else //lista non vuota addSon(x, tree->right); } Algoritmi e strutture dati 30 7.2 Esempi di programmi su alberi generici: inserimento 8. Alberi binari di ricerca: definizione inserisce son come ultimo figlio di father . Se l’albero e’ vuoto, lo inserisce come radice Un albero binario di ricerca è un albero binario tale che per ogni nodo p: int insert(InfoType son, InfoType father, Node* &tree) { if (!tree) { // albero vuoto tree=new Node; • i nodi del sottoalbero sinistro di p hanno etichetta minore dell’etichetta di p tree->label=son; tree->left = tree->right = NULL; return 1; } Node* a=findNode(father, tree); // a: puntatore di father if (!a) return 0; • i nodi del sottoalbero destro di p hanno etichetta maggiore dell’etichetta di p // father non trovato addSon(son, a->left); return 1; } Algoritmi e strutture dati 31 8. Un albero binario di ricerca Algoritmi e strutture dati 32 8. Un albero binario di ricerca con gli stessi nodi 50 50 80 35 80 20 20 10 35 90 90 10 Algoritmi e strutture dati 33 8. Un albero binario di ricerca con gli stessi nodi Algoritmi e strutture dati 34 8. Alberi binari di ricerca: proprietà e operazioni • non ci sono doppioni 90 • la visita simmetrica elenca le etichette in ordine crescente 80 OPERAZIONI 50 • ricerca di un nodo 35 • inserimento di un nodo • cancellazione di un nodo 20 10 Algoritmi e strutture dati 35 Algoritmi e strutture dati 36 8. Alberi binari di ricerca: ricerca 8. Alberi binari di ricerca: ricerca T(0)=a Node* findNode (InfoType n, Node* tree) { if (!tree) return 0; // albero vuoto T(n)= b + T(k) k<n T(0)=a if (n == tree->label ) return tree; if (n<tree->label ) return findNode(n, tree->left); return findNode(n, tree->right); // n=radice O(log n) T(n)= b + T(n/2) // n<radice T(0)=a O(n) T(n)= b + T(n-1) // n>radice } in media : O(logn) Algoritmi e strutture dati 37 Algoritmi e strutture dati 8. Alberi binari di ricerca: inserimento 38 8. Esempio di inserimento 50 void insertNode (InfoType n, Node* &tree) { if (!tree) { // albero vuoto : creazione nodo tree=new Node; tree->label=n; tree->left = tree->right = NULL; return; } if (n<tree->label ) // n<radice insertNode (n, tree->left); if (n>tree->label ) // n>radice insertNode (n, tree->right); } 80 20 10 35 90 50 inserisco 40 80 20 10 35 40 O(log n) Algoritmi e strutture dati 39 Algoritmi e strutture dati 8. Alberi binari di ricerca: cancellazione 40 8. Alberi binari di ricerca: cancellazione restituisce l’etichetta del nodo più piccolo di un albero ed elimina il nodo che la contiene void deleteNode(InfoType n, Node* &tree) { if (tree) if (n < tree->label) void deleteMin (Node* &tree, InfoType &m) { if (tree->left) //c’è un nodo più piccolo deleteMin(tree->left, m); else { m=tree->label; //restitusco l’etichetta Node* a=tree; tree=tree->right; //connetto il sottoalbero destro di //n minore della radice { deleteNode(n, tree->left); return; } if (n > tree->label) //n maggiore della radice { deleteNode(n, tree->right); return; } if (!tree->left) //n non ha figlio sinistro { Node* a=tree; tree=tree->right; delete a;return;} if (!tree->right) //n non ha figlio destro { Node* a=tree; tree=tree->left; delete a; return;} // m al padre di m delete a; 90 deleteMin(tree->right, tree->label); //elimino il nodo //n ha entrambi i figli } } } O(log n) Algoritmi e strutture dati 41 Algoritmi e strutture dati 42 8. Esempio di cancellazione 100 100 50 50 40 40 200 200 80 80 100 100 70 70 60 60 cancello 50 60 60 65 65 40 40 200 200 80 80 70 70 65 65 Algoritmi e strutture dati 43