Alberi 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, 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 foglia radice cammino sottoalbero 3 4 Alberi: definizione ricorsiva 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 Perché non si ammette l albero vuoto? Quale è la differenza tra una lista vuota di sottoalberi e una lista non vuota di sottoalberi vuoti? Alberi: Alcuni concetti Grado di un nodo: numero di figli del nodo Cammino: sequenza di nodi <n 0, n 1,, n k > dove il nodo n i è padre del nodo n i+1, per 0 i < k 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 Definizione ricorsiva: il livello della radice è 0, il livello di un nodo non radice è 1 + il livello del padre Altezza dell albero: la lunghezza del più lungo cammino nell albero Parte dalla radice e termina in una foglia 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) Costruzione dell albero 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 Alberi: prima Realizzazione Ogni nodo ha un reference all oggetto che costituisce l informazione e un reference alla lista di sottoalberi 4 1 8 6 9 7 1 6 4 8 9 7 Programmazione Object-Oriented in Java 1
7 8 Classe SimpleTree import java.io.*; Import java.util.*; class SimpleTree { public Object elem; public SimpleList subtrees; // classe già nota // definizione dei metodi private Node rootnode; // radice dell albero SimpleTree: Costruttore 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; 9 10 SimpleTree: Altri metodi public Object root() { return rootnode.elem; public SimpleList subtreelist() { return rootnode.subtrees; Visite di alberi Un albero può essere visitato (attraversato) secondo le seguenti modalità 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, e così via Poiché non è importante l ordine di visita, ogni metodo di visita restituisce una Enumeration La classe Tree include le classi, PostOrderTraversal, BreadthFirstTraversal che implementano delle Enumeration 11 1 7 9 Visite 9 7 Visite di alberi: Metodi public Enumeration preorderelements() { return new (); 4 3 11 1 8 6 14 18 13 16 17 1 11 3 4 6 8 Preordine:, 7, 4, 3, 8, 6, 11, 1,, 9, 14, 13, 16, 17, 18 Postordine: 4, 8, 6, 3, 11, 1, 7,, 13, 16, 17, 14, 18, 9, Per Livelli:, 7,, 9, 4, 3, 11, 1, 14, 18, 8, 6, 13, 16, 17 18 14 17 16 13 public Enumeration postorderelements() { return new PostOrderTraversal(); public Enumeration BreadthFirstElements () { return new BreadthFirstTraversal (); Programmazione Object-Oriented in Java
13 14 class implements Enumeration { public () { public boolean hasmoreelements() { public Object nextelement() { private void preorderbuild(node n) { private Queue queueelements; 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 public () { queueelements = new Queue(); if (rootnode!= null) preorderbuild(rootnode); 1 16 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 18 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 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 (prima realizzazione) Programmazione Object-Oriented in Java 3
19 0 PostOrderTraversal 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 BreadthFirstTraversal class BreadthFirstTraversal implements Enumeration { public BreadthFirstTraversal () { public boolean hasmoreelements() { public Object nextelement() { private Queue nodes; Molto simile alla seconda realizzazione di, 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 1 BreadthFirstTraversal public BreadthFirstTraversal () { nodes = new Queue(); if (rootnode!= null) nodes.addelement(rootnode); public boolean hasmoreelements() { if (nodes.isempty()) return false; return true; 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; 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 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 3 Classe 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 private Object data; // informazione associata al nodo private List children = new List(); // in alternativa usare un Vector // inizializzazione all atto dell instanziazione 4 Programmazione Object-Oriented in Java 4
6 Classe Node: altri metodi public Node addchild(object data) { Node tempnode = new Node(data); children.inserthead(tempnode); return tempnode; Classe Tree 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 Esercizo 7 Alberi n-ary 8 Realizzare la gestione di un file system 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 Cancellazione, creazione di una directory Un nodo può avere al più n figli Ogni figlio e individuato da una posizione 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 Realizzazione semplificata Nodo incapsulato nella definizione della classe albero Costruzione dell albero top-down Dicotomia Nodo-Informazione 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 9 Alberi Binari import java.io.*; import java.util.*; class SimpleBTree { 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 30 Programmazione Object-Oriented in Java
4 Esempio 31 public SimpleBTree() { SimpleBTree: Costruttore 3 7 1 1 3 4 9 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; 33 34 SimpleBTree: Altri metodi public boolean isempty() { return rootnode == null; public Object root() { if (rootnode == null) return null; return rootnode.elem; SimpleBTree: Altri metodi public SimpleBTree leftbtree() { if (rootnode == null) return null; SimpleBTree tmp = new SimpleBTree(); tmp.rootnode = rootnode.left; return tmp; public SimpleBTree rightbtree() { if (rootnode == null) return null; SimpleBTree tmp = new SimpleBTree(); tmp.rootnode = rootnode.right; return tmp; Visite di alberi binari 3 Visite di alberi binari: Metodi 36 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 public Enumeration preorderelements() { return new (); public Enumeration postorderelements() { return new PostOrderTraversal(); public Enumeration InOrderElements () { return new InOrderTraversal (); Programmazione Object-Oriented in Java 6
Esercizi 37 Rappresentazione binaria di alberi 38 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, N alb, esiste un corrispondente nell albero binario, N bin al primo figlio di N alb corrisponde il figlio sinistro di N bin al fratello successivo di N alb corrisponde il figlio destro di N bin 7 9 4 3 11 1 8 6 Esempio 14 18 13 16 17 7 4 3 9 8 11 14 6 1 13 18 La visita in preordine dell albero binario a sinistra è equivalente alla visita in preordine dell albero a destra 16 17 39 Rappresentazione binaria di alberi Ogni nodo contiene la radice reference al primo figlio reference al fratello successivo public Object elem; // informazione associata al nodo public Tree firstsubtree; // primo sottoalbero public Tree next; // prossimo sottoalbero fratello 40 1 Esempio 41 1 3 4 6 7 8 9 10 11 1 Esercizi Realizzare la classe SimpleTree utilizzando la rappresentazione binaria dell albero 4 3 4 6 7 8 9 10 11 1 Programmazione Object-Oriented in Java 7