Revisões – Java Revisões – Complexidade Árvores Binárias de Pesquisa (revisões) Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Algoritmos e Estruturas de Dados II – Licenciatura em Engenharia Informática e Computação – www.fe.up.pt/∼rcamacho/cadeiras/AED2 Rui Camacho LIACC/FEUP Universidade do Porto [email protected] Fevereiro 2005 Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Árvores Conjunto de nós e conjunto de arestas que ligam pares de nós I Um nó é a raiz I Com excepção da raiz, todo o nó está ligado por uma aresta a 1 e 1 só nó (o pai) I Há um caminho único da raiz a cada nó; o tamanho do caminho para um nó é o número de arestas a percorrer I Nós sem descendentes: folhas Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Árvores Binárias de Pesquisa Sumário I Definição. I Árvore binária pesquisa I I I I Interface Nó de árvore Implementação Iteradores de árvore I Visita em pré-ordem, pós-ordem, in-ordem Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Interface de árvore de pesquisa // // // // // // // // // // // // // // // SearchTree interface ************PUBLIC OPERATIONS*************** void insert( x) → Insert x void remove( x) → Remove x void removeMin() → Remove smallest item Comparable find( x) → Return item that matches x Comparable findMin() → Return smallest item Comparable findMax() → Return largest item boolean isEmpty() → Return true if empty; else false void makeEmpty() → Remove all items void printTree() → Print tree in sorted order ************ERRORS************************** Most routines throw ItemNotFound on various degenerate conditions insert throws DuplicateItem if item is already in the tree Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Árvores Binárias de pesquisa I Pesquisa em elementos ordenados: pode ser feita em O(log n) I I Manter o tempo de acesso logarı́tmico com inserção e remoção I I I ... sem inserção ou remoção de elementos estrutura em árvore binária mais operações do que árvore básica: pesquisar, inserir, remover Objectos nos nós devem ser comparáveis I interface Comparable: objectos de classes que a implementam podem ser comparados Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Nó de árvore binária class BinaryNode{ // Friendly data accessible by other package routines Comparable element; // The data in the node BinaryNode left; // Left child BinaryNode right; // Right child BinaryNode(Comparable theElement){ this(theElement, null, null); } BinaryNode(Comparable theElement, BinaryNode lt, BinaryNode rt){ element = theElement; left = lt; right = rt; } } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Interface de árvore de pesquisa public interface SearchTree{ void insert(Comparable x) throws DuplicateItem; void remove(Comparable x) throws ItemNotFound; void removeMin() throws ItemNotFound; Comparable findMin() throws ItemNotFound; Comparable findMax() throws ItemNotFound; Comparable find(Comparable x) throws ItemNotFound; void makeEmpty(); boolean isEmpty(); void printTree(); } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Implementação de árvore binária public void removeMin() throws ItemNotFound{ root = removeMin(root); } public Comparable findMin() throws ItemNotFound{ return findMin(root).element; } public Comparable findMax() throws ItemNotFound{ return findMax(root).element; } public Comparable find(Comparable x) throws ItemNotFound{ return find(x, root).element; } public void makeEmpty(){ root = null; } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Implementação de árvore binária public class BinarySearchTree implements SearchTree{ /** The tree root. */ protected BinaryNode root; public BinarySearchTree(){ root = null; } public void insert(Comparable x) throws DuplicateItem{ root = insert(x, root); } public void remove(Comparable x) throws ItemNotFound{ root = remove(x, root); } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Implementação de árvore binária protected BinaryNode remove(Comparable x, BinaryNode t) throws ItemNotFound{ if(t == null) throw new ItemNotFound(”SearchTree remove”); if(x.compares(t.element) < 0) t.left = remove(x, t.left); else if(x.compares(t.element) > 0) t.right = remove(x, t.right); else if(t.left != null && t.right != null){ // Two children t.element = findMin(t.right).element; t.right = removeMin(t.right); } else t = (t.left != null) ? t.left : t.right; return t; } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Implementação de árvore binária public boolean isEmpty(){ return root == null; } public void printTree(){ if(root == null) System.out.println(”Empty tree”); else printTree(root); } protected BinaryNode insert(Comparable x, BinaryNode t) throws DuplicateItem{ if(t == null) t = new BinaryNode(x, null, null); else if(x.compares(t.element) < 0) t.left = insert(x, t.left); else if(x.compares(t.element) > 0) t.right = insert(x, t.right); else throw new DuplicateItem(”SearchTree insert”); return t; } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Implementação de árvore binária protected BinaryNode findMax(BinaryNode t) throws ItemNotFound{ if(t == null) throw new ItemNotFound(”SearchTree findMax”); while(t.right != null) t = t.right; return t; } protected BinaryNode find(Comparable x, BinaryNode t) throws ItemNotFound{ while(t != null) if(x.compares(t.element) < 0) t = t.left; else if(x.compares(t.element) > 0) t = t.right; else return t; // Match throw new ItemNotFound(”SearchTree find”); } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Implementação de árvore binária protected BinaryNode removeMin(BinaryNode t) throws ItemNotFound{ if(t == null) throw new ItemNotFound(”SearchTree removeMin”); if(t.left != null) t.left = removeMin(t.left); else t = t.right; return t; } protected BinaryNode findMin(BinaryNode t) throws ItemNotFound{ if(t == null) throw new ItemNotFound(”SearchTree findMin”); while(t.left != null) t = t.left; return t; } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Iterador em árvore binária I Operação: percorrer todos os nós da árvore, por ordem escolhida I I I pré-ordem: um nó é visitado, seguindo-se a visita dos seus filhos pós-ordem: nó é visitado após a visita dos seus filhos in-ordem: nó é visitado entre as visitas do filho esquerdo e do filho direito I Visita da árvore implementada por uma aplicação I I a visita pode exprimir-se recursivamente de forma muito simples o contexto de execução do programa (na stack) mantém a informação necessária para realizar a visita na ordem pretendida I Visita da árvore encapsulada na classe de um iterador I I contexto tem de ser mantido pelo iterador iterador gere uma pilha para manter registo do estado da visita Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Implementação de árvore binária protected void printTree(BinaryNode t){ if(t != null){ printTree(t.left); System.out.println(t.element.toString()); printTree(t.right); } } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Implementação de iterador de árvore final public Object retrieve() throws ItemNotFound{ if(current == null) throw new ItemNotFound(”TreeIterator retrieve”); return current.element; } abstract public void advance() throws ItemNotFound; Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Implementação de iterador de árvore abstract public class TreeIterator{ protected BinarySearchTree t; // Tree protected BinaryNode current; // Current position public TreeIterator(BinarySearchTree theTree){ t = theTree; current = null; } abstract public void first(); final public boolean isValid(){ return current != null; } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Iterador em pré-ordem public class PreOrder extends TreeIterator{ private Stack s; // Stack of TreeNode objects public PreOrder(BinarySearchTree theTree){ super(theTree); s = new StackAr(); s.push(t.root); } public void first(){ s.makeEmpty(); if(t.root != null) s.push(t.root); try{ advance(); } catch(ItemNotFound e){ } // Empty tree } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Nó na pilha do iterador package DataStructures; // An internal class for tree iterators class StNode{ BinaryNode node; int timesPopped; StNode(BinaryNode n){ node = n; timesPopped = 0; } } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Iterador em pós-ordem public class PostOrder extends TreeIterator{ protected Stack s; // The stack of StNode objects public PostOrder(BinarySearchTree theTree){ super(theTree); s = new StackAr(); s.push(new StNode(t.root)); } public void first(){ s.makeEmpty(); if(t.root != null) s.push(new StNode(t.root)); try{ advance(); } catch(ItemNotFound e){ } // Empty tree } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Iterador em pré-ordem public void advance() throws ItemNotFound{ if(s.isEmpty()){ if(current == null) throw new ItemNotFound(”PreOrder Advance”); current = null; return; } try{ current = (BinaryNode) s.topAndPop(); } catch(Underflow e){ return; } // Cannot happen if(current.right != null) s.push(current.right); if(current.left != null) s.push(current.left); } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Iterador em in-ordem public class InOrder extends PostOrder{ public InOrder( BinarySearchTree theTree){ super(theTree); } for(; ;){ try{ cnode = (StNode) s.topAndPop(); } catch(Underflow e){ return; } if(++cnode.timesPopped == 2){ current = cnode.node; if(cnode.node.right != null) s.push(new StNode(cnode.node.right)); return; } public void advance() throws ItemNotFound{ if(s.isEmpty()){ if(current == null) throw new ItemNotFound( ” InOrder Advance”); current = null; return; } StNode cnode; ... // First time through s.push(cnode); if(cnode.node.left != null) s.push(new StNode(cnode.node.left)); } } } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Iterador em pós-ordem public void advance() throws ItemNotFound{ if(s.isEmpty()){ if(current == null) throw new ItemNotFound( ”PostOrder Advance”); current = null; return; } StNode cnode; ... for(; ;){ try{ cnode = (StNode) s.topAndPop(); } catch(Underflow e){ return; } if(++cnode.timesPopped == 3){ current = cnode.node; return; } s.push(cnode); if(cnode.timesPopped == 1){ if(cnode.node.left != null) s.push(new StNode(cnode.node.left)); } else // cnode.timesPopped == 2 { if(cnode.node.right != null) s.push(new StNode(cnode.node.right)); } } } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Iterador em ordem de nı́vel public void advance() throws ItemNotFound{ if(q.isEmpty()){ if(current == null) throw new ItemNotFound(”LevelOrder advance”); current = null; return; } try{ current = (BinaryNode) q.dequeue(); } catch(Underflow E){ return; } // Cannot happen if(current.left != null) q.enqueue(current.left); if(current.right != null) q.enqueue(current.right); } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Iterador em ordem de nı́vel public class LevelOrder extends TreeIterator{ private Queue q; // Queue of TreeNode objects public LevelOrder(BinarySearchTree theTree){ super(theTree); q = new QueueAr(); q.enqueue(t.root); } public void first(){ q.makeEmpty(); if(t.root != null) q.enqueue(t.root); try{ advance(); } catch(ItemNotFound e){ } // Empty tree } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Testar iterador de árvore public static void main(String[ ] args){ BinarySearchTree t = new BinarySearchTree(); testItr(”PreOrder”, new PreOrder(t)); try{ t.insert(new MyInteger(4)); t.insert(new MyInteger(2)); t.insert(new MyInteger(6)); t.insert(new MyInteger(1)); t.insert(new MyInteger(3)); t.insert(new MyInteger(5)); t.insert(new MyInteger(7)); } catch(Exception e){ } testItr(”Preorder”, new PreOrder(t)); testItr(”Postorder”, new PostOrder(t)); testItr(”Inorder”, new InOrder(t)); testItr(”Level order”, new LevelOrder(t)); } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC Revisões – Java Revisões – Complexidade Testar o iterador public static void testItr(String type, TreeIterator itr){ try{ System.out.print(type + ”:”); for(itr.first(); itr.isValid(); itr.advance()) System.out.print(” ” + itr.retrieve()); System.out.println(); itr.advance(); } catch(ItemNotFound E){ System.out.println(E + ” (as expected)”); } } Rui Camacho Algoritmos e Estruturas de Dados II – LEIC