From 51465f1701947f6020cbdff7035138fd976db98b Mon Sep 17 00:00:00 2001 From: Fabio Scotto di Santolo Date: Fri, 2 Mar 2018 10:56:01 +0100 Subject: [PATCH] add tree implementations --- src/it/algoritmi/adt/BinaryTree.java | 15 + src/it/algoritmi/adt/Tree.java | 36 +++ .../adt/tree/AbstractBinaryTree.java | 53 +++ src/it/algoritmi/adt/tree/AbstractTree.java | 65 ++++ .../algoritmi/adt/tree/ArrayBinaryTree.java | 120 +++++++ .../algoritmi/adt/tree/LinkedBinaryTree.java | 306 ++++++++++++++++++ 6 files changed, 595 insertions(+) create mode 100644 src/it/algoritmi/adt/BinaryTree.java create mode 100644 src/it/algoritmi/adt/Tree.java create mode 100644 src/it/algoritmi/adt/tree/AbstractBinaryTree.java create mode 100644 src/it/algoritmi/adt/tree/AbstractTree.java create mode 100644 src/it/algoritmi/adt/tree/ArrayBinaryTree.java create mode 100644 src/it/algoritmi/adt/tree/LinkedBinaryTree.java diff --git a/src/it/algoritmi/adt/BinaryTree.java b/src/it/algoritmi/adt/BinaryTree.java new file mode 100644 index 0000000..45c5920 --- /dev/null +++ b/src/it/algoritmi/adt/BinaryTree.java @@ -0,0 +1,15 @@ +package it.algoritmi.adt; + +/** Un albero binario */ +public interface BinaryTree extends Tree { + + /** Restituisce la posizione del figlio sinistro di position (o null se non esiste). */ + Position left(Position position) throws IllegalArgumentException; + + /** Restituisce la posizione del figlio destro di position (o null se non esiste). */ + Position right(Position position) throws IllegalArgumentException; + + /** Restituisce la posizione del fratello di position (o null se non esiste). */ + Position sibling(Position position) throws IllegalArgumentException; + +} diff --git a/src/it/algoritmi/adt/Tree.java b/src/it/algoritmi/adt/Tree.java new file mode 100644 index 0000000..32120a1 --- /dev/null +++ b/src/it/algoritmi/adt/Tree.java @@ -0,0 +1,36 @@ +package it.algoritmi.adt; + +/** Un albero i cui nodi possono avere un numero di figli arbitrario. */ +public interface Tree extends Iterable { + + /** Restituisce la posizione della radice dell'albero. */ + Position root(); + + /** Restituisce la posizione del parent del nodo alla posizione position. */ + Position parent(Position position) throws IllegalArgumentException; + + /** Restituisce una struttura iterabile dei figli del nodo alla posizione position. */ + Iterable> children(Position position) throws IllegalArgumentException; + + /** Restituisce il numero di figli del nodo alla posizione position. */ + int numChildren(Position position) throws IllegalArgumentException; + + /** Restituisce true se e solo se il nodo alla posizione position non è foglia. */ + boolean isInternal(Position position) throws IllegalArgumentException; + + /** Restituisce true se e solo se il nodo alla posizione position è foglia. */ + boolean isExternal(Position position) throws IllegalArgumentException; + + /** Restituisce true se e solo se il nodo alla posizione position è la radice. */ + boolean isRoot(Position position) throws IllegalArgumentException; + + /** Restituisce il numero di elementi presenti nella struttura. */ + int size(); + + /** Restituisce true se e solo se la struttura è vuota. */ + boolean isEmpty(); + + /** Restituisce un contenitore iterabile con tutte le posizioni dell'albero. */ + Iterable> positions(); + +} diff --git a/src/it/algoritmi/adt/tree/AbstractBinaryTree.java b/src/it/algoritmi/adt/tree/AbstractBinaryTree.java new file mode 100644 index 0000000..add0b46 --- /dev/null +++ b/src/it/algoritmi/adt/tree/AbstractBinaryTree.java @@ -0,0 +1,53 @@ +package it.algoritmi.adt.tree; + +import java.util.ArrayList; +import java.util.List; + +import it.algoritmi.adt.BinaryTree; +import it.algoritmi.adt.Position; + +/** Una classe di base astratta che implementain parte l'interfaccia BinaryTree */ +public abstract class AbstractBinaryTree extends AbstractTree implements BinaryTree { + + /** + * Restituisce la posizione del fratello di position (o null se non esiste). + * @param position il nodo che vuoi ispezionare + * @return la posizione del fratello di posistion o null se non esiste + */ + @Override + public Position sibling(Position position) { + Position parent = parent(position); + if(parent == null) return null; // position è la radice + if(position == left(position)) // position è il figlio sinistro + return right(position); // può essere null + else // position è il figlio destro + return left(parent); // può essere null + } + + /** + * Restituisce il numero di figli della Position position + * @param position + * @return restituisce il numero di figli della Position position + */ + @Override + public int numChildren(Position position) { + int count = 0; + if(left(position) != null) ++count; + if(right(position) != null) ++count; + return count; + } + + /** + * Restituisce un contenitore iterabile delle posizione dei figli di position + * @param position + * @return restituisce un contenitore iterabile delle posizione dei figli di position + */ + @Override + public Iterable> children(Position position) { + List> snapshot = new ArrayList<>(2); // capacità massima + if(left(position) != null) snapshot.add(left(position)); + if(right(position) != null) snapshot.add(right(position)); + return snapshot; + } + +} diff --git a/src/it/algoritmi/adt/tree/AbstractTree.java b/src/it/algoritmi/adt/tree/AbstractTree.java new file mode 100644 index 0000000..0040706 --- /dev/null +++ b/src/it/algoritmi/adt/tree/AbstractTree.java @@ -0,0 +1,65 @@ +package it.algoritmi.adt.tree; + +import it.algoritmi.adt.Position; +import it.algoritmi.adt.Tree; + +/** Una classe di base astratta che implementa in parte l'interfaccia Tree. */ +public abstract class AbstractTree implements Tree { + + @Override + public boolean isInternal(Position position) throws IllegalArgumentException { + return numChildren(position) > 0; + } + + @Override + public boolean isExternal(Position position) throws IllegalArgumentException { + return numChildren(position) == 0; + } + + @Override + public boolean isRoot(Position position) throws IllegalArgumentException { + return position == root(); + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + /** Restituisce la profondità della Position position. */ + public int depth(Position position) { + class A { + int depth(Position position, int acc) { + return isRoot(position) ? 0 : depth(parent(position), ++acc); + } + } + return (new A()).depth(position, 0); + } + + /** + * Restituisce l'altezza dell'albero. + * @return altezza dell'albero + */ + @Deprecated + public int height() { + int h = 0; + for(Position position : positions()) + if(isExternal(position)) + h = Math.max(h, depth(position)); + return h; + } + + /** + * Restituisce l'altezza del sottoalbero avente + * radice nelle Position position. + * @param position + * @return altezza dell'albero + */ + public int height(Position position) { + int h = 0; + for(Position c : children(position)) + h = Math.max(h, 1 + height(c)); + return h; + } + +} diff --git a/src/it/algoritmi/adt/tree/ArrayBinaryTree.java b/src/it/algoritmi/adt/tree/ArrayBinaryTree.java new file mode 100644 index 0000000..05f19a6 --- /dev/null +++ b/src/it/algoritmi/adt/tree/ArrayBinaryTree.java @@ -0,0 +1,120 @@ +package it.algoritmi.adt.tree; + +import java.util.Iterator; + +import it.algoritmi.adt.Position; + +public class ArrayBinaryTree extends AbstractBinaryTree { + + //----------------- classe Node annidata ------------------------- + protected static class Node implements Position { + private E element; + private Node parent; + private Node left; + private Node right; + + public Node(E element, Node above, Node leftChild, Node rightChild) { + this.element = element; + this.parent = above; + this.left = leftChild; + this.right = rightChild; + } + + @Override + public E getElement() { + return element; + } + + public void setElement(E element) { + this.element = element; + } + + public Node getParent() { + return parent; + } + + public void setParent(Node parent) { + this.parent = parent; + } + + public Node getLeft() { + return left; + } + + public void setLeft(Node left) { + this.left = left; + } + + public Node getRight() { + return right; + } + + public void setRight(Node right) { + this.right = right; + } + } + //----------------- fine della classe Node annidata ------------------- + + public static final int CAPACITY = 16; + + private Node[] data; + private int size; + + public ArrayBinaryTree() { + this(CAPACITY); + } + + @SuppressWarnings("unchecked") + public ArrayBinaryTree(int capacity) { + data = (Node[]) new Object[capacity]; + size = 0; + } + + @Override + public Position left(Position position) throws IllegalArgumentException { + // TODO Auto-generated method stub + return null; + } + + @Override + public Position right(Position position) throws IllegalArgumentException { + // TODO Auto-generated method stub + return null; + } + + @Override + public Position root() { + return data[0]; + } + + @Override + public Position parent(Position position) throws IllegalArgumentException { + if(isRoot(position)) return null; + int index = depth(position); + + return null; + } + + /** + * Restituisce il numero di nodi presenti + * nell'albero. + * @return numero di nodi dell'albero + */ + @Override + public int size() { + return size; + } + + @Override + public Iterable> positions() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Iterator iterator() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/it/algoritmi/adt/tree/LinkedBinaryTree.java b/src/it/algoritmi/adt/tree/LinkedBinaryTree.java new file mode 100644 index 0000000..d69e441 --- /dev/null +++ b/src/it/algoritmi/adt/tree/LinkedBinaryTree.java @@ -0,0 +1,306 @@ +package it.algoritmi.adt.tree; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import it.algoritmi.adt.Position; + +/** Implementazione concreta di albero binario usando nodi concatenati. */ +public class LinkedBinaryTree extends AbstractBinaryTree { + + //----------------- classe Node annidata ------------------------- + protected static class Node implements Position { + private E element; + private Node parent; + private Node left; + private Node right; + + public Node(E element, Node above, Node leftChild, Node rightChild) { + this.element = element; + this.parent = above; + this.left = leftChild; + this.right = rightChild; + } + + @Override + public E getElement() { + return element; + } + + public void setElement(E element) { + this.element = element; + } + + public Node getParent() { + return parent; + } + + public void setParent(Node parent) { + this.parent = parent; + } + + public Node getLeft() { + return left; + } + + public void setLeft(Node left) { + this.left = left; + } + + public Node getRight() { + return right; + } + + public void setRight(Node right) { + this.right = right; + } + } + //----------------- fine della classe Node annidata ------------------- + + /** Metodo-fabbrica che crea un nuovo nodo memorizzandovi l'elemento element */ + protected Node createNode(E element, Node parent, Node left, Node right) { + return new Node<>(element, parent, left, right); + } + + protected Node root; + private int size; + + /** Verifica la validità della posizione e la restituisce sotto forma di nodo. */ + protected Node validate(Position position) throws IllegalArgumentException { + if(!(position instanceof Node)) throw new IllegalArgumentException("Not valid position type"); + Node node = (Node) position; + if(node.getParent() == node) throw new IllegalArgumentException("position is no longer in the tree"); + return node; + } + + /** + * Restituisce la Position del figlio sinistro di position + * (o null se non c'è). + * @param position + * @return figlio sinistro o null se non c'è + */ + @Override + public Position left(Position position) throws IllegalArgumentException { + Node node = validate(position); + return node.getLeft(); + } + + /** + * Restituisce la Position del figlio destro di position + * (o null se non c'è). + * @param position + * @return figlio destro o null se non c'è + */ + @Override + public Position right(Position position) throws IllegalArgumentException { + Node node = validate(position); + return node.getRight(); + } + + /** + * Restituisce la Position radice dell'albero (o null se l'albero è vuoto). + * @return radice dell'albero o null se è vuoto + */ + @Override + public Position root() { + return root; + } + + /** + * Restituisce la Position del genitore di position + * (o null se position è la radice). + * @param position + * @return posizione di position o null se è radice + */ + @Override + public Position parent(Position position) throws IllegalArgumentException { + Node node = validate(position); + return node.getParent(); + } + + /** + * Restituisce il numero di nodi presenti + * nell'albero. + * @return numero di nodi dell'albero + */ + @Override + public int size() { + return size; + } + + /** + * Pone e nelle radice di un albero vuoto e + * restituisce la sua nuova Position + */ + public Position addRoot(E element) throws IllegalArgumentException { + if(!isEmpty()) throw new IllegalStateException("Tree is not empty"); + root = createNode(element, null, null, null); + this.size = 1; + return root; + } + + /** + * Crea il figlio sinistro di position con l'elemento element e; + * ne restituisce la posizione. + * @param position + * @param element + * @return la posizione del figlio sinistro creato. + * @throws IllegalArgumentException + */ + public Position addLeft(Position position, E element) throws IllegalArgumentException { + Node parent = validate(position); + if(parent.getLeft() != null) throw new IllegalArgumentException("position already has a left child"); + Node child = createNode(element, parent, null, null); + parent.setLeft(child); + ++this.size; + return child; + } + + /** + * Crea il figlio destro di position con l'elemento element e; + * ne resituisce la posizione. + * @param position + * @param element + * @return la posizione del figlio destro creato. + * @throws IllegalArgumentException + */ + public Position addRight(Position position, E element) throws IllegalArgumentException { + Node parent = validate(position); + if(parent.getRight() != null) throw new IllegalArgumentException("position already has a right child"); + Node child = createNode(element, parent, null, null); + parent.setRight(child); + ++this.size; + return child; + } + + /** + * Sostituisce con element l'elemento in position + * e restituisce l'elemento sostituito. + * @param position + * @param element + * @return l'elemento sostituito + * @throws IllegalArgumentException + */ + public E set(Position position, E element) throws IllegalArgumentException { + Node node = validate(position); + E tmp = node.getElement(); + node.setElement(element); + return tmp; + } + + /** + * Collega gli alberi t1 e t2 come sottoalberi + * sinistro e destro di position, foglia. + * @param position + * @param t1 + * @param t2 + * @throws IllegalArgumentException + */ + public void attach(Position position, LinkedBinaryTree t1, LinkedBinaryTree t2) throws IllegalArgumentException { + Node node = validate(position); + if(isInternal(position)) throw new IllegalArgumentException("position must be a leaf"); + size += t1.size() + t2.size(); + if(!t1.isEmpty()) { // collega t1 come sottoalbero sinistro di node + t1.root.setParent(node); + node.setLeft(t1.root); + t2.root = null; + t2.size = 0; + } + if(!t2.isEmpty()) { + t2.root.setParent(node); + node.setRight(t2.root); + t1.root = null; + t1.size = 0; + } + } + + /** + * Elimina il nodo in posizione position e + * lo sostituisce con il figlo, se c'è. + * @param position + * @return elemento eliminato + * @throws IllegalArgumentException + */ + public E remove(Position position) throws IllegalArgumentException { + Node node = validate(position); + if(numChildren(position) == 2) throw new IllegalArgumentException("position has two children"); + Node child = (node.getLeft() != null ? node.getLeft() : node.getRight()); + if(child != null) child.setParent(node.getParent()); // il nonno di child ne diventa genitore + if(node == root) + root = child; // child diventa la radice + else { + Node parent = node.getParent(); + if(node == parent.getLeft()) parent.setLeft(child); + else parent.setRight(child); + } + --size; + E tmp = node.getElement(); + node.setElement(null); + node.setLeft(null); + node.setParent(node); + return tmp; + } + + /** + * Aggiunge a snapshot le posizioni del sottoalbero avente radice position. + * @param position + * @param snapshot + */ + private void preorderSubtree(Position position, List> snapshot) { + snapshot.add(position); // in pre-ordine, aggiungiamo position prima dei sottoalberi + for(Position c : children(position)) + preorderSubtree(c, snapshot); + } + + /** + * Restituisce una lista delle posizioni dell'albero, in pre-ordine. + * @return lista delle posizioni in pre-ordine + */ + protected Iterable> preorder() { + List> snapshot = new ArrayList<>(); + if(!isEmpty()) preorderSubtree(root(), snapshot); // riempe ricorsivamente snapshot + return snapshot; + } + + /** + * Restituisce una lista delle posizioni dell'albero + * @return lista delle posizioni. + */ + @Override + public Iterable> positions() { + return preorder(); + } + + //------------------ classe ElementIterator annidata ---------- + /** Adatta l'iterazione prodotta da positions() per restituire elementi. */ + private class ElementIterator implements Iterator { + Iterator> it = positions().iterator(); + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public E next() { + return it.next().getElement(); + } + + @Override + public void remove() { + it.remove(); + } + } + + /** + * Restituisce un iteratore degli elementi + * memorizzati nell'albero. + * @return iteratore alla struttura + */ + @Override + public Iterator iterator() { + return new ElementIterator(); + } + +}