Facoltà di Ingegneria
2
Alberi
Ogni nodo ha un unico arco entrante, tranne un nodo
particolare, chiamato radice, che non ha archi entranti;
Ogni nodo può avere zero o più archi uscenti
I nodi senza archi uscenti sono detti foglie
Un arco nell’albero induce una relazione padre-figlio
L’ordine dei figli di un nodo non ha importanza
Strutture dati: Alberi
Strutture gerarchiche di dati
Esempi
– Il file system di un sistema operativo
– L’organigramma di un’azienda
– Alberi generali, alberi n-ari, alberi binari, …
radice
sottoalbero
cammino
foglia
3
4
Alberi: definizione ricorsiva
Alberi: Alcuni concetti
Grado di un nodo: numero di figli del nodo
Cammino: sequenza di nodi <n0, n1, …, nk> dove il nodo ni è padre
del nodo ni+1, per 0 i < k
Un albero può essere definito come una coppia (r,
s) dove r è un nodo e s è una lista di sottoalberi
– La lista di sottoalberi può essere eventualmente vuota e
ciò ferma la ricorsione
– La lunghezza del cammino è k
– Dato un nodo, esiste un unico cammino dalla radice dell’albero al nodo
Livello di un nodo: lunghezza del cammino dalla radice al nodo
Perché non si ammette l’albero vuoto ?
– Definizione ricorsiva: il livello della radice è 0, il livello di un nodo non radice è 1
+ il livello del padre
– Quale è la differenza tra una lista vuota di sottoalberi e una lista
non vuota di sottoalberi vuoti ?
Altezza dell’albero: la lunghezza del più lungo cammino nell’albero
– Parte dalla radice e termina in una foglia
5
6
Alberi: Una prima realizzazione
Un albero dovrebbe fornire metodi per accedere
– all’informazione contenuta nella radice dell’albero
– alla lista dei sottoalberi (una SimpleList)
Alberi: prima Realizzazione
Ogni nodo ha un reference all’oggetto che costituisce
l’informazione e un reference alla lista di sottoalberi
Costruzione dell’albero
2
– una prima strategia: bottom-up
– Dato una informazione e una lista di alberi, costruire un nuovo
albero che ha come radice e come lista di sottoalberi
l’informazione e la lista in ingresso
– In questo caso definiamo il set di operatori minimali e si parla di
SimpleTree
Per attraversare un albero e per costruire è necessario
usare i metodi della classe SimpleList
Programmazione Object-Oriented in Java
2
1
4
6
8
4
5
9
5
6
1
8
9
7
7
1
Facoltà di Ingegneria
7
Classe SimpleTree
SimpleTree: Costruttore
import java.io.*;
Import java.util.*;
class SimpleTree {
class Node {
public Object elem;
public SimpleList subTrees; // classe già nota
}
// definizione dei metodi
private Node rootNode;
}
8
public SimpleTree(Object rootInfo) {
rootNode = new Node();
rootNode.elem = rootInfo;
rootNode.subTrees = null; // per default
}
public SimpleTree(Object rootInfo, SimpleList subTrees) {
rootNode = new Node();
rootNode.elem = rootInfo;
rootNode.subTrees = subTrees;
}
// radice dell’albero
9
10
Visite di alberi
SimpleTree: Altri metodi
Un albero può essere visitato (attraversato) secondo le
seguenti modalità
public Object root() {
return rootNode.elem;
}
– Preordine: visita prima la radice e poi i sottoalberi (completamente
uno alla volta)
– Postordine: visita prima i sottoalberi (completamente uno alla
volta) e poi la radice
– Per livelli: visita prima la radice, poi i nodi di livello 1 (figli della
radice), poi quelli di livello 2, e così via
public SimpleList subTreeList() {
return rootNode.subTrees;
}
Poiché non è importante l’ordine di visita, ogni metodo di
visita restituisce una Enumeration
– La classe Tree include le classi PreOrderTraversal,
PostOrderTraversal, BreadthFirstTraversal che implementano
delle Enumeration
11
Visite
7
4
3
8
2
11
6
9
9
21
14
13
16
18 14
18
17
17
16
Visite di alberi: Metodi
5
5
2
public Enumeration preOrderElements() {
return new PreOrderTraversal();
}
7
21
11
13
6
Preordine: 5, 7, 4, 3, 8, 6, 11, 21, 2, 9, 14, 13, 16, 17, 18
Postordine: 4, 8, 6, 3, 11, 21, 7, 2, 13, 16, 17, 14, 18, 9, 5
Per Livelli: 5, 7, 2, 9, 4, 3, 11, 21, 14, 18, 8, 6, 13, 16, 17
Programmazione Object-Oriented in Java
12
3
4
8
public Enumeration postOrderElements() {
return new PostOrderTraversal();
}
public Enumeration BreadthFirstElements () {
return new BreadthFirstTraversal ();
}
2
Facoltà di Ingegneria
13
PreOrderTraversal
14
PreOrderTraversal
class PreOrderTraversal implements Enumeration {
public PreOrderTraversal () { … }
public boolean hasMoreElements() { … }
public Object nextElement() { … }
private void preOrderBuild(Node n) { … }
private Queue queueElements;
public PreOrderTraversal () {
queueElements = new Queue();
if (rootNode != null)
preOrderBuild(rootNode);
}
}
Usiamo una coda, in cui il costruttore memorizza tutte le
informazioni dell’albero in pre-order
– Il costruttore usa il metodo ricorsivo preOrderBuild per
memorizzare le informazioni dell’albero nella coda
15
PreOrderTraversal
16
PreOrderTraversal
private void preOrderBuild(Node n) { // metodo ricorsivo
if (n != null) {
queueElements.addElement(n.elem); // inserisce la radice
SimpleList list = n.subTrees;
if(list != null)
while(! list.isEmpty()) { // visita dei sottoalberi
SimpleTree t = (SimpleTree) list.head();
preOrderBuild(t.rootNode); // ricorsione
list.removeHead();
}
}
}
public boolean hasMoreElements() {
return queueElements.isEmpty();
}
public Object nextElement() {
Object el = queueElements.firstElement();
queueElements.removeElement();
return el;
}
Semplice scansione della coda
17
PostOrderTraversal
class PostOrderTraversal implements Enumeration {
public PostOrderTraversal () { … }
public boolean hasMoreElements() { … }
public Object nextElement() { … }
private void postOrderBuild(Node n) { … }
private Queue queueElements;
}
Usiamo una coda, in cui il costruttore memorizza tutte le
informazioni dell’albero in post-order
– Il costruttore usa il metodo ricorsivo postOrderBuild per
memorizzare le informazioni dell’albero nella coda
Programmazione Object-Oriented in Java
18
PostOrderTraversal
public PostOrderTraversal () {
queueElements = new Queue();
if (rootNode != null)
postOrderBuild(rootNode);
}
La realizzazione di hasMoreElements e di
nextElements è identica a quanto vista per la classe
PreOrderTraversal (prima realizzazione)
3
Facoltà di Ingegneria
19
20
PostOrderTraversal
BreadthFirstTraversal
private void postOrderBuild(Node n) { // metodo ricorsivo
if (n != null) {
SimpleList list = n.subTrees;
if(list != null)
while(! list.isEmpty()) { // visita dei sottoalberi
SimpleTree t = (SimpleTree) list.head();
postOrderBuild(t.rootNode); // ricorsione
list.removeHead();
}
queueElements.addElement(n.elem); // inserisce la radice
}
}
class BreadthFirstTraversal implements Enumeration {
public BreadthFirstTraversal () { … }
public boolean hasMoreElements() { … }
public Object nextElement() { … }
private Queue nodes;
}
Molto simile alla seconda realizzazione di PreOrderTraversal,
solo che usa una coda invece di uno stack
– Il metodo nextElement() elimina il nodo dal front della coda,
inserisce i sottoalberi (eventualmente) in coda e restituisce il nodo
21
22
BreadthFirstTraversal
BreadthFirstTraversal
public Object nextElement() {
if (nodes.isEmpty()) return null;
Node currentNode = (Node) nodes.firstElement();
nodes.removeElement();
SimpleList list = currentNode.subTrees;
if(list != null)
while(! list.isEmpty()) {
SimpleTree t = (SimpleTree) list.head();
nodes.addElement(t.rootNode);
list.removeHead();
}
return currentNode.elem;
}
public BreadthFirstTraversal () {
nodes = new Queue();
if (rootNode != null)
nodes.addElement(rootNode);
}
public boolean hasMoreElements() {
if (nodes.isEmpty()) return false;
return true;
}
23
24
Alberi: Una diversa realizzazione
Costruzione dell’albero
– strategia: top-down
– aggiungere un figlio ad un nodo dell’albero
In questo caso la classe Node non può essere incapsulata
(nascosta) all’interno della classe Tree, ma occorre
esplicitare la dicotomia dei concetti di nodo e informazione
associata, presenti nell’albero
Classe Node
class Node {
public Node() { }
public Node(Object data) { this.data = data; }
public void setData(Object data) { this.data = data; }
public Object getData() { return data; }
public List getChildren() { return children; }
// altri metodi
– La gestione dell’albero avviene usando la classe Node, ossia
aggiungendo e/o rimuovendo figli a nodi
– La classe Tree serve solo a mantenere il nodo radice e a
realizzare le visite dell’albero
private Object data; // informazione associata al nodo
private List children = new List(); // in alternativa usare un Vector
// inizializzazione all’atto dell’instanziazione
}
Programmazione Object-Oriented in Java
4
Facoltà di Ingegneria
25
26
Classe Node: altri metodi
Classe Tree
public Node addChild(Object data) {
Node tempNode = new Node(data);
children.insertHead(tempNode);
return tempNode;
}
import java.io.*;
Import java.util.*;
class Tree {
public Tree() {rootNode = new Node(); }
public Tree(Object rootInfo) { rootNode = new Node(rootInfo); }
public Node root() { return rootNode; }
public void deleteSubTree(Node child) {
int pos = children.indexOf(child);
if (pos >=0) children.removeElementAt(pos);
// si può migliorare
}
// realizzazioni delle Traversal … farlo come esercizio
private Node rootNode;
// radice dell’albero
}
27
Esercizo
28
Alberi n-ary
Realizzare la gestione di un file system
Un nodo può avere al più n figli
Ogni figlio e’ individuato da una posizione
– Struttura gerarchica delle directory
– Uno stack per mantenere il path della directory corrente,
oppure un reference da ogni nodo al nodo padre
– Implementazione delle operazioni
– Ciascun figlio può essere presente o meno (in questo caso è
ammesso l’albero vuoto)
– Un albero è vuoto o è dato da una radice e una n-pla di sottoalberi
– La lista dei figli è realizzata come un Vector o un array
Costruzione dell’albero bottom-up
• Cancellazione, creazione di una directory
– Realizzazione semplificata
– Nodo incapsulato nella definizione della classe albero
Costruzione dell’albero top-down
– Dicotomia Nodo-Informazione
29
Alberi binari
Particolari alberi n-ari con caratteristiche molto importanti
Ogni nodo può avere al più due figli
– sottoalbero sinistro e sottoalbero destro
Definizione ricorsiva:
– un albero binario è vuoto
– oppure è una terna (s, r, d), dove r è un nodo (la radice), s e d
sono alberi binari
Alberi binari semplificati
– Costruttore bottom-up
– Operatori di selezione
– Operatori di visita
Programmazione Object-Oriented in Java
30
Alberi Binari
import java.io.*;
import java.util.*;
class SimpleBTree {
class Node {
public Object elem; // informazione associata al nodo
public Node left; // sottoalbero sinistro
public Node right; // sottoalbero destro
}
// definizione dei metodi
private Node rootNode = null;
}
// radice dell’albero
5
Facoltà di Ingegneria
31
Esempio
SimpleBTree: Costruttore
4
7
32
public SimpleBTree() { }
public SimpleBTree(Object rootInfo, SimpleBTree left, SimpleBTree right) {
rootNode = new Node();
rootNode.elem = rootInfo;
if (left == null) rootNode.left = null;
else rootNode.left = left.rootNode;
if (right == null) rootNode.right = null;
else rootNode.right = right.rootNode;
}
12
3
1
4
9
33
SimpleBTree: Altri metodi
34
SimpleBTree: Altri metodi
public SimpleBTree leftBTree() {
if (rootNode == null) return null;
SimpleBTree tmp = new SimpleBTree();
tmp.rootNode = rootNode.left;
return tmp;
}
public boolean isEmpty() {
return rootNode == null;
}
public Object root() {
if (rootNode == null) return null;
return rootNode.elem;
}
public SimpleBTree rightBTree() {
if (rootNode == null) return null;
SimpleBTree tmp = new SimpleBTree();
tmp.rootNode = rootNode.right;
return tmp;
}
35
Visite di alberi binari
Oltre alle visite classiche di un albero
– Preordine
– Postordine
– Per livelli
Un ulteriore algoritmo di visita applicabile ad un albero
binario è la visita simmetrica (In-Order)
– visita prima il sottoalbero sinistro, poi la radice, e infine il
sottoalbero destro
Realizzazioni di classi Traversal che implementano
Enumeration
Programmazione Object-Oriented in Java
36
Visite di alberi binari: Metodi
public Enumeration preOrderElements() {
return new PreOrderTraversal();
}
public Enumeration postOrderElements() {
return new PostOrderTraversal();
}
public Enumeration InOrderElements () {
return new InOrderTraversal ();
}
6
Facoltà di Ingegneria
37
Esercizi
38
Rappresentazione binaria di alberi
Realizzare le tre visite dell’albero binario in maniera
ricorsiva, in maniera simile a quanto visto per le
visite in preorder e postorder dell’albero generale
Ai soli fini della equivalenza della visita in preordine
un albero può essere rappresentato mediante un
albero binario equivalente
– per ogni nodo dell’albero, Nalb, esiste un corrispondente
nell’albero binario, Nbin
– al primo figlio di Nalb corrisponde il figlio sinistro di Nbin
– al fratello successivo di Nalb corrisponde il figlio destro di Nbin
39
5
Esempio
40
Rappresentazione binaria di alberi
7
Ogni nodo contiene
5
2
4
7
2
9
11
8
4
3
8
21
11
6
14
13
18
14
21 13
6
18
class Node {
public Object elem; // informazione associata al nodo
public Tree firstSubTree; // primo sottoalbero
public Tree next;
// prossimo sottoalbero fratello
}
16
17
16
– la radice
– reference al primo figlio
– reference al fratello successivo
9
3
17
La visita in preordine dell’albero binario a sinistra è
equivalente alla visita in preordine dell’albero a destra
41
1
Esercizi
Esempio
2
5
1
10
2
5
10
3
6
4
7
11
8
9
Realizzare la classe SimpleTree utilizzando la
rappresentazione binaria dell’albero
12
4
7
11
3
6
42
8
9
12
Programmazione Object-Oriented in Java
7