Title: Nessun titolo diapositiva
1FONDAMENTI DI INFORMATICA II
Ingegneria Gestionale
a.a. 2001-2002 - 4 Ciclo
Alberi
2Alberi
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
dellalbero. 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 con
entrambi i link a l valore 0, viene detto nodo
foglia.
3Alberi
Rappresentazione grafica di un albero
Puntatore esterno al nodo radice
Nodo radice
A
C
B
Nodo foglia
Nodo figlio
Sottoalbero di destra
D
4Alberi
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 dallalto verso il basso.
Lesempio che segue è quello di un albero con un
valore intero per nodo
5Alberi
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
lordine in cui i valori sono inseriti
nellalbero.
6Alberi
Esempio di albero binario di ricerca
7Alberi
Funzioni ricorsive Si dicono funzioni ricorsive
quelle funzioni che richiamano se stesse. Quando
una funzione ricorsiva invoca se stessa la sua
elaborazione sinterrompe 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 unultima
istanza della funzione riesce a completare i
calcoli senza invocare se stessa essa può far
ritornare il controllo allistanza che lha
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.
8Alberi
9Alberi
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 è
10Alberi
// Classe TreeNode per la generazione dei nodi di
un albero ifndef TREENODE_H define
TREENODE_H templatelt class NODETYPE gt class
Tree // forward declaration templatelt class
NODETYPE gt class TreeNode friend class Treelt
NODETYPE gt public TreeNode( const NODETYPE
d ) leftPtr( 0 ), data( d ), rightPtr( 0 )
NODETYPE getData() const return data
private TreeNodelt NODETYPE gt leftPtr //
pointer to left subtree TreeNodelt NODETYPE gt
rightPtr // pointer to right subtree
NODETYPE data endif
11Alberi
// Definition of template class Tree ifndef
TREE_H define TREE_H include ltiostreamgt include
ltcassertgt include "treenode.h" using
stdendl templatelt class NODETYPE gt class Tree
public Tree( ) void insertNode( const
NODETYPE ) void preOrderTraversal( )
const void inOrderTraversal( ) const void
postOrderTraversal( ) const private
TreeNodelt NODETYPE gt rootPtr
12Alberi
// utility functions void insertNodeHelper(
TreeNodelt NODETYPE gt , const NODETYPE
) void preOrderHelper( TreeNodelt NODETYPE gt
) const void inOrderHelper( TreeNodelt
NODETYPE gt ) const void postOrderHelper(
TreeNodelt NODETYPE gt ) const templatelt
class NODETYPE gt Treelt NODETYPE gtTree( )
rootPtr 0 templatelt class NODETYPE gt void
Treelt NODETYPE gtinsertNode( const NODETYPE
value ) insertNodeHelper( rootPtr, value )
13Alberi
// This function receives a pointer to a pointer
so the // pointer can be modified. templatelt
class NODETYPE gt void Treelt NODETYPE
gtinsertNodeHelper( TreeNodelt NODETYPE
gt ptr, const NODETYPE value ) if ( ptr
0 ) // tree (or sub tree)
is empty ptr new TreeNodelt NODETYPE gt
( value ) assert( ptr ! 0 ) else
// tree is not empty
if ( value lt ( ptr )-gtdata )
insertNodeHelper( ( ( ptr )-gtleftPtr ), value
) else if ( value gt ( ptr
)-gtdata ) insertNodeHelper( ( ( ptr
)-gtrightPtr ), value ) else
cout ltlt value ltlt " dup" ltlt endl
14Alberi
templatelt class NODETYPE gt void Treelt NODETYPE
gtpreOrderTraversal() const preOrderHelper(
rootPtr ) templatelt class NODETYPE gt void
Treelt NODETYPE gtpreOrderHelper(
TreeNodelt NODETYPE gt ptr ) const
if ( ptr ! 0 ) cout ltlt ptr-gtdata ltlt '
' preOrderHelper( ptr-gtleftPtr )
preOrderHelper( ptr-gtrightPtr )
15Alberi
templatelt class NODETYPE gt void Treelt NODETYPE
gtinOrderTraversal() const inOrderHelper(
rootPtr ) templatelt class NODETYPE gt void
Treelt NODETYPE gtinOrderHelper(
TreeNodelt NODETYPE gt ptr ) const if (
ptr ! 0 ) inOrderHelper( ptr-gtleftPtr
) cout ltlt ptr-gtdata ltlt ' '
inOrderHelper( ptr-gtrightPtr )
16Alberi
templatelt class NODETYPE gt void Treelt NODETYPE
gtpostOrderTraversal() const
postOrderHelper( rootPtr ) templatelt class
NODETYPE gt void Treelt NODETYPE gtpostOrderHelper(
TreeNodelt NODETYPE gt
ptr ) const if ( ptr ! 0 )
postOrderHelper( ptr-gtleftPtr )
postOrderHelper( ptr-gtrightPtr ) cout ltlt
ptr-gtdata ltlt ' ' endif
17Alberi
// Driver to test class Tree include
ltiostreamgt include ltiomanipgt include
"tree.h" using stdcout using stdcin using
stdsetiosflags using stdios using
stdsetprecision int main() Treelt int gt
intTree int intVal, i cout ltlt "Enter 10
integer values\n" for( i 0 i lt 10 i )
cin gtgt intVal intTree.insertNode(
intVal )
18Alberi
cout ltlt "\nPreorder traversal\n"
intTree.preOrderTraversal() cout ltlt
"\nInorder traversal\n" intTree.inOrderTravers
al() cout ltlt "\nPostorder traversal\n"
intTree.postOrderTraversal() Treelt double gt
doubleTree double doubleVal cout ltlt
"\n\n\nEnter 10 double values\n" ltlt
setiosflags( iosfixed iosshowpoint )
ltlt setprecision( 1 ) for ( i 0 i lt 10
i ) cin gtgt doubleVal
doubleTree.insertNode( doubleVal )
19Alberi
cout ltlt "\nPreorder traversal\n"
doubleTree.preOrderTraversal() cout ltlt
"\nInorder traversal\n" doubleTree.inOrderTrav
ersal() cout ltlt "\nPostorder traversal\n"
doubleTree.postOrderTraversal() return
0
20Alberi
Enter 10 integer values 50 25 75 12 33 67
88 6 13 68 Preorder traversal 50 25 12 6
13 33 75 67 68 88 Inorder traversal 6 12
13 25 33 50 67 68 75 88 Postorder
traversal 6 13 12 33 25 68 67 88 75
50 Enter 10 double values 39.2 16.5 82.7 3.3
65.2 90.8 1.1 4.4 89.5 92.5 Preorder
traversal 39.2 16.5 3.3 1.1 4.4 82.7 65.2
90.8 89.5 92.5 Inorder traversal 1.1 3.3 4.4
16.5 39.2 65.2 82.7 89.5 90.8
92.5 Postorder traversal 1.1 4.4 3.3 16.5
65.2 89.5 92.5 90.8 82.7 39.2
21Alberi
Rappresentazione grafica dellalbero ottenuto con
i 10 valori interi
22Alberi
Visite ricorsive Il grafico che segue rappresenta
le visite ricorsive dei rami dellalbero
effettuate con le funzioni preOrderTraversal,
inOrderTraversal e postOrderTraversal. Ogni nodo
è rappresentato con un cerchietto contenente il
dato (intero) in esso immagazzinato. Alluscita
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 preOrder prima della prima uscita dal
nodo. inOrder prima della seconda uscita dal
nodo. postOrder dopo il rientro definitivo nel
nodo
23Alberi
33
88
0
0
0
0
13
68
0
0
0
0
0
0
0
24Alberi
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 2k -1. Dato un albero con n elementi su k
livelli essi saranno n 20 21 22 23... 2k
-1 2k-1 per cui approssimativamente il numero di
livelli di un albero di ricerca ben bilanciato è
pari a log2n. Ciò vuol dire che con 1000 elementi
lalbero ha circa 10 livelli (infatti 210gt1000),
ossia una ricerca su 1000 elementi non richiede
più di 10 confronti.