FONDAMENTI DI INFORMATICA II Ingegneria Gestionale a a

  • Slides: 24
Download presentation
FONDAMENTI DI INFORMATICA II Ingegneria Gestionale a. a. 2001 -2002 - 4° Ciclo Alberi

FONDAMENTI DI INFORMATICA II Ingegneria Gestionale a. a. 2001 -2002 - 4° Ciclo Alberi 1

Alberi Definizione Un albero è una struttura dati non lineare perché i suoi nodi

Alberi Definizione Un albero è una struttura dati non lineare perché i suoi nodi contengono due o più membri di link. Sono alberi binari quelli che contengono in ogni nodo esattamente due membri di link. Elementi di un albero binario Il nodo radice è il primo nodo di un albero, generalmente puntato da un puntatore esterno alla struttura dell’albero. I link del nodo radice, che convenzionalmente indicheremo come link di sinistra o link di destra, possono puntare a due altri nodi, detti nodi figli, o assumere il valore 0. Se esistono i nodi figli essi possono considerarsi come primi nodi rispettivamente del sottoalbero di sinistra e del sottoalbero di destra. I suddetti nodi figli possono a loro volta puntare ad altri nodi figli e ciascuno di essi può essere considerato il primo di un sottoalbero. Un nodo senza figli, ossia 2 con entrambi i link a l valore 0, viene detto nodo foglia.

Alberi Rappresentazione grafica di un albero Puntatore esterno al nodo radice Nodo foglia B

Alberi Rappresentazione grafica di un albero Puntatore esterno al nodo radice Nodo foglia B A Nodo figlio C Sottoalbero di destra D 3

Alberi Rappresentazione grafica semplificata di un albero Nella rappresentazione grafica semplificata di un albero

Alberi Rappresentazione grafica semplificata di un albero Nella rappresentazione grafica semplificata di un albero vengono presentati, senza particolari riquadrature, solo i dati contenuti nei nodi e, per mezzo di segmenti, i link tra i nodi. Un link mancante corrisponde nel nodo ad un puntatore di valore 0. Il nodo radice viene presentato in alto e, per convenzione i link sono diretti dall’alto verso il basso. L’esempio che segue è quello di un albero con un valore intero per nodo: 10 15 11 20 13 17 19 21 4

Alberi Albero binario di ricerca Un albero binario di ricerca è caratterizzato dal fatto

Alberi Albero binario di ricerca Un albero binario di ricerca è caratterizzato dal fatto che, dato un qualunque nodo, i valori dei dati contenuti nel sottoalbero di sinistra sono inferiori al valore del dato presente nel nodo in esame, che, a sua volta, è minore dei valori dei dati contenuti nel sottoalbero di destra. Nota: La forma di un albero di ricerca che corrisponde ad un insieme di dati non è unica: essa può variare secondo l’ordine in cui i valori sono inseriti nell’albero. 5

Alberi Esempio di albero binario di ricerca 47 25 11 7 77 43 17

Alberi Esempio di albero binario di ricerca 47 25 11 7 77 43 17 31 65 44 93 68 6

Alberi Funzioni ricorsive Si dicono funzioni ricorsive quelle funzioni che richiamano se stesse. Quando

Alberi Funzioni ricorsive Si dicono funzioni ricorsive quelle funzioni che richiamano se stesse. Quando una funzione ricorsiva invoca se stessa la sua elaborazione s’interrompe a quel punto e il controllo viene ceduto ad una nuova istanza della funzione, che, a sua volta, può richiamare se stessa e quindi cedere il controllo ad una nuova istanza, …ecc. Se ad un certo punto un’ultima istanza della funzione riesce a completare i calcoli senza invocare se stessa può far ritornare il controllo all’istanza che l’ha invocata, la quale può completare i calcoli e restituire a sua volta il controllo alla precedente istanza e così via fino alla prima. Una funzione ricorsiva deve avere quindi uno schema di operazioni del tipo descritto con lo schema a blocchi che segue. 7

Alberi Funz_Ricors (parametro) Si Elaborazioni del caso base Parametro rela tivo al caso base

Alberi Funz_Ricors (parametro) Si Elaborazioni del caso base Parametro rela tivo al caso base No Elaborazioni Richiamo Funz_Ricors con parametro tendente al caso base return 8

Alberi Esempio di funzione ricorsiva Funzione per il calcolo del fattoriale di un numero:

Alberi Esempio di funzione ricorsiva Funzione per il calcolo del fattoriale di un numero: int factorial (int number) { if (number= =1)return 1; else return number * factorial (number-1); } Si può dare una rappresentazione grafica del funzionamento di una funzione ricorsiva indicando con una freccia il momento del suo richiamo ricorsivo, con un cerchietto, contenente il valore del parametro, la sua istanziazione e con un arco accompagnato dal valore del risultato il momento della restituzione del controllo. Nel caso della funzione factorial a cui sia stato passato il parametro number= 4, questa rappresentazione è: 4 4*6 3 3*2 2 2*1 1 1 9

Alberi // Classe Tree. Node per la generazione dei nodi di un albero #ifndef

Alberi // Classe Tree. Node per la generazione dei nodi di un albero #ifndef TREENODE_H #define TREENODE_H template< class NODETYPE > class Tree; // forward declaration template< class NODETYPE > class Tree. Node { friend class Tree< NODETYPE >; public: Tree. Node( const NODETYPE &d ) : left. Ptr( 0 ), data( d ), right. Ptr( 0 ) { } NODETYPE get. Data() const { return data; } private: Tree. Node< NODETYPE > *left. Ptr; // pointer to left subtree Tree. Node< NODETYPE > *right. Ptr; // pointer to right subtree NODETYPE data; }; 10 #endif

Alberi // Definition of template class Tree #ifndef TREE_H #define TREE_H #include <iostream> #include

Alberi // Definition of template class Tree #ifndef TREE_H #define TREE_H #include <iostream> #include <cassert> #include "treenode. h" using std: : endl; template< class NODETYPE > class Tree { public: Tree( ); void insert. Node( const NODETYPE & ); void pre. Order. Traversal( ) const; void in. Order. Traversal( ) const; void post. Order. Traversal( ) const; private: Tree. Node< NODETYPE > *root. Ptr; 11

Alberi // utility functions void insert. Node. Helper( Tree. Node< NODETYPE > **, const

Alberi // utility functions void insert. Node. Helper( Tree. Node< NODETYPE > **, const NODETYPE & ); void pre. Order. Helper( Tree. Node< NODETYPE > * ) const; void in. Order. Helper( Tree. Node< NODETYPE > * ) const; void post. Order. Helper( Tree. Node< NODETYPE > * ) const; }; template< class NODETYPE > Tree< NODETYPE >: : Tree( ) { root. Ptr = 0; } template< class NODETYPE > void Tree< NODETYPE >: : insert. Node( const NODETYPE & value ) 12 { insert. Node. Helper( &root. Ptr, value ); }

Alberi // This function receives a pointer to a pointer so the // pointer

Alberi // This function receives a pointer to a pointer so the // pointer can be modified. template< class NODETYPE > void Tree< NODETYPE >: : insert. Node. Helper( Tree. Node< NODETYPE > **ptr, const NODETYPE &value ) { if ( *ptr == 0 ) { // tree (or sub tree) is empty *ptr = new Tree. Node< NODETYPE > ( value ); assert( *ptr != 0 ); } else // tree is not empty if ( value < ( *ptr )->data ) insert. Node. Helper( &( ( *ptr )->left. Ptr ), value ); else if ( value > ( *ptr )->data ) insert. Node. Helper( &( ( *ptr )->right. Ptr ), value ); else cout << value << " dup" << endl; } 13

Alberi template< class NODETYPE > void Tree< NODETYPE >: : pre. Order. Traversal() const

Alberi template< class NODETYPE > void Tree< NODETYPE >: : pre. Order. Traversal() const { pre. Order. Helper( root. Ptr ); } template< class NODETYPE > void Tree< NODETYPE >: : pre. Order. Helper( Tree. Node< NODETYPE > *ptr ) const { if ( ptr != 0 ) { cout << ptr->data << ' '; pre. Order. Helper( ptr->left. Ptr ); pre. Order. Helper( ptr->right. Ptr ); } } 14

Alberi template< class NODETYPE > void Tree< NODETYPE >: : in. Order. Traversal() const

Alberi template< class NODETYPE > void Tree< NODETYPE >: : in. Order. Traversal() const { in. Order. Helper( root. Ptr ); } template< class NODETYPE > void Tree< NODETYPE >: : in. Order. Helper( Tree. Node< NODETYPE > *ptr ) const {if ( ptr != 0 ) { in. Order. Helper( ptr->left. Ptr ); cout << ptr->data << ' '; in. Order. Helper( ptr->right. Ptr ); } } 15

Alberi template< class NODETYPE > void Tree< NODETYPE >: : post. Order. Traversal() const

Alberi template< class NODETYPE > void Tree< NODETYPE >: : post. Order. Traversal() const { post. Order. Helper( root. Ptr ); } template< class NODETYPE > void Tree< NODETYPE >: : post. Order. Helper( Tree. Node< NODETYPE > *ptr ) const { if ( ptr != 0 ) { post. Order. Helper( ptr->left. Ptr ); post. Order. Helper( ptr->right. Ptr ); cout << ptr->data << ' '; } } #endif 16

Alberi // Driver to test class Tree #include <iostream> #include <iomanip> #include "tree. h"

Alberi // Driver to test class Tree #include <iostream> #include <iomanip> #include "tree. h" using std: : cout; using std: : cin; using std: : setiosflags; using std: : ios; using std: : setprecision; int main() { Tree< int > int. Tree; int. Val, i; cout << "Enter 10 integer values: n"; for( i = 0; i < 10; i++ ) { cin >> int. Val; int. Tree. insert. Node( int. Val ); } 17

Alberi cout << "n. Preorder traversaln"; int. Tree. pre. Order. Traversal(); cout << "n.

Alberi cout << "n. Preorder traversaln"; int. Tree. pre. Order. Traversal(); cout << "n. Inorder traversaln"; int. Tree. in. Order. Traversal(); cout << "n. Postorder traversaln"; int. Tree. post. Order. Traversal(); Tree< double > double. Tree; double. Val; cout << "nnn. Enter 10 double values: n" << setiosflags( ios: : fixed | ios: : showpoint ) << setprecision( 1 ); for ( i = 0; i < 10; i++ ) { cin >> double. Val; double. Tree. insert. Node( double. Val ); } 18

Alberi cout << "n. Preorder traversaln"; double. Tree. pre. Order. Traversal(); cout << "n.

Alberi cout << "n. Preorder traversaln"; double. Tree. pre. Order. Traversal(); cout << "n. Inorder traversaln"; double. Tree. in. Order. Traversal(); cout << "n. Postorder traversaln"; double. Tree. post. Order. Traversal(); } return 0; 19

Alberi Enter 10 integer values: 50 25 75 12 33 67 88 6 Preorder

Alberi Enter 10 integer values: 50 25 75 12 33 67 88 6 Preorder traversal 50 25 12 6 13 33 75 67 Inorder traversal 6 12 13 25 33 50 67 68 Postorder traversal 6 13 12 33 25 68 67 88 13 68 68 88 75 50 Enter 10 double values: 39. 2 16. 5 82. 7 3. 3 65. 2 90. 8 1. 1 4. 4 Preorder traversal 39. 2 16. 5 3. 3 1. 1 4. 4 82. 7 65. 2 90. 8 Inorder traversal 1. 1 3. 3 4. 4 16. 5 39. 2 65. 2 82. 7 89. 5 Postorder traversal 1. 1 4. 4 3. 3 16. 5 65. 2 89. 5 92. 5 90. 8 92. 5 82. 7 39. 2 20

Alberi Rappresentazione grafica dell’albero ottenuto con i 10 valori interi 50 25 12 6

Alberi Rappresentazione grafica dell’albero ottenuto con i 10 valori interi 50 25 12 6 75 33 13 67 88 68 21

Alberi Visite ricorsive Il grafico che segue rappresenta le visite ricorsive dei rami dell’albero

Alberi Visite ricorsive Il grafico che segue rappresenta le visite ricorsive dei rami dell’albero effettuate con le funzioni pre. Order. Traversal, in. Order. Traversal e post. Order. Traversal. Ogni nodo è rappresentato con un cerchietto contenente il dato (intero) in esso immagazzinato. All’uscita da ciascun nodo la visita al ramo di sinistra è rappresentata da una freccia verticale, mentre la visita al ramo di destra da una freccia inclinata. Il ritorno dopo una visita è rappresentato con un arco. Nelle funzioni le visite ai rami di sinistra precedono sempre quelle dei rami di destra. Nelle tre funzioni le elaborazioni del dato del nodo sono eseguite come segue: pre. Order: prima della prima uscita dal nodo. in. Order: prima della seconda uscita dal nodo. post. Order: dopo il rientro definitivo nel nodo 22

Alberi 50 25 75 33 88 12 67 0 13 6 0 0 0

Alberi 50 25 75 33 88 12 67 0 13 6 0 0 0 0 68 0 0 0 23

Alberi Importanza degli alberi binari di ricerca In un albero di ricerca ben bilanciato

Alberi Importanza degli alberi binari di ricerca In un albero di ricerca ben bilanciato ogni livello conterrà un numero di elementi pari a circa il doppio degli elementi del livello precedente. Gli elementi di ogni livello crescono perciò con le potenze di 2 per cui al livello k gli elementi saranno 2 k -1. Dato un albero con n elementi su k livelli essi saranno: n= 20+ 21+ 22+ 23+. . . +2 k -1= 2 k-1 per cui approssimativamente il numero di livelli di un albero di ricerca ben bilanciato è pari a log 2 n. Ciò vuol dire che con 1000 elementi l’albero ha circa 10 livelli (infatti 210>1000), ossia una ricerca su 1000 elementi non richiede più di 10 confronti. 24