Tipos de Dados Abstratos Algoritmos e Estruturas de Dados Verão 2012 1
Tipos Abstratos Um tipo abstrato é: um tipo genérico de dados, dos quais não se conhece os valores uma interface que define os acessos e as operações sobre os dados O conjunto das propriedades algébricas da interface, que delimitam as possíveis implementações. Um programa que usa um tipo abstrato é um cliente. O cliente não tem acesso à implementação. Um programa que especifique o tipo de dados é uma implementação. 2
Pilha - Stack Operações abstratas: inicialização; colocação de um elemento na pilha; remoção de um elemento (o último colocado); LIFO indicar se pilha está vazia. interface para uma pilha que contenha inteiros: public interface intstack{ public boolean empty(); public void push(int i); várias public int pop(); Usando a mesma interface, podem ser definidas implementações! 3
Pilha - Stack Operações abstratas: inicialização; colocação de um elemento na pilha; remoção de um elemento (o último colocado); LIFO indicar se pilha está vazia. interface para uma pilha genérica: public interface Stack< E > { public boolean isempty(); public E push(e i); várias implementações! public E pop(); public E peek(); Usando a mesma interface, podem ser definidas 4
Pilha (Stack) - inserção 5
Pilha (Stack) - remoção 6
Implementação do tipo pilha usando um array //Não compila!! public class StackArray<E> implements Stack<E>{ private E[] s; private int top; public StackArray(int capacity) { s=new E[capacity]; //! a criação de um array genérico não é permitida em Java public boolean isempty(){ return top == 0; public E peek() { if(isempty()) throw new EmptyStackException(); return s[top-1]; public E push(e item) { if( top == s.length ) return null; s[top++] = item; return item; public E pop() { if(isempty()) throw new EmptyStackException(); E e = s[--top]; s[top] = null; return e; 7
Implementação do tipo pilha usando um array public class StackArray<E> implements Stack<E>{ private E[] s; private int top; public StackArray(int capacity) { s= ( E[] ) new Object[capacity]; //solução! public boolean isempty(){ return top == 0; public E peek() { if(isempty()) throw new EmptyStackException(); return s[top-1]; public E push(e item) { if( top == s.length ) return null; s[top++] = item; return item; public E pop() { if(isempty()) throw new EmptyStackException(); E e = s[--top]; s[top] = null; return e; Vantagens: simplicidade. Desvantagens: tamanho máximo limitado à partida. 8
Lista Simplesmente Ligada Uma lista ligada é uma colecção de objectos, designados por nós, em que cada um contêm dados e apenas uma referência para outro nó, de modo a formarem uma lista. O primeiro nó é designado por head node. Uma lista ligada diz-se simplesmente ligada quando cada nó contém uma referência para o próximo nó. private class Node<E>{ private E data; private Node next;... Exemplo: :SLinkedList head :Node item next c :Node item next g null 9
Implementação do tipo pilha usando um lista ligada public class StackList<E> implements Stack<E>{ private static class Node<E> { E item; Node<E> next; Node(E i, Node<E> n){ item=i; next = n; private Node<E> head; public boolean isempty() {return head == null; public E peek() { if(isempty()) throw new EmptyStackException(); return head.item; public E push(e item) { head = new Node<E>(item, head); return item; public E pop() { if(isempty()) throw new EmptyStackException(); E item = head.item; head = head.next; return item; Vantagens: - aumenta e diminui consoante se inserem ou removem novos elementos na pilha. Desvantagens: - ocupa mais memória. - acesso mais lento. 10
Que estrutura de dados escolher? Depende : das operações que sejam mais frequentes no algoritmo a implementar: Se a escolha for entre arrays e listas, dever-se-á escolher: arrays se forem predominantes as operações de acesso e modificação; listas se forem predominantes as operações de inserção e remoção. Usar listas simples, se estas operações ocorrem sempre no início. Usar listas simples com ponteiro adicional para o fim da lista, se a inserção se fizer no fim e a remoção no início. da previsibilidade sobre a memória necessária: o uso de arrays pressupõe que se saiba que memória é suficiente; quando não é previsível é necessário gerir a memória dinamicamente. 11
PriorityQueue public interface PriorityQueue<E>{ public void add(e elem, P prio, KeyExtractor<E> keyextractor); public E pick(); public E poll(); public E update(int key, P prio); public void remove(int key); public interface KeyExtractor<E>{ public int getkey(e e); 12
Fila-Queue Operações abstratas: inicialização; colocação de um elemento na fila; remoção de um elemento (FIFO); indicar se fila está vazia. interface para uma fila : public interface Queue<E>{ public boolean isempty(); public boolean offer(e i); public E poll(); public E remove(); public E peek(); Usando a mesma interface, podem ser definidas várias implementações! 13
Fila (Queue) - inserção 14
Fila (Queue) - remoção 15
Implementação do tipo fila usando um array public class QueueArray<E> implements Queue<E> { private E[] q; private int size, head, tail; public QueueArray(int maxn) { q=(e[])new Object[maxN]; public boolean isempty() { return size == 0 ; public boolean offer(e e){ if( size == q.length) return false; q[tail] = e; tail = (tail+1) % q.length; ++size; return true; public E remove() { if(isempty()) throw new NoSuchElementException(); E e= q[head]; q[head]= null; head= (head+1)%q.length; --size; return e; public E poll(){ return ( size == 0 )? null : remove(); public E peek() {return ( size == 0 )? null : q[head]; 16
Implementação do tipo fila usando uma lista ligada public class QueueList<E> implements Queue<E> { private static class Node<E> { E item; Node<E> next; Node() { next=this; Node(E i, Node<E> n) { item=i; next = n; private Node<E> tail = new Node<E>(); public boolean isempty() { return tail == tail.next; public boolean offer(e e){ tail.next = new Node<E>( e, tail.next ); tail = tail.next; return true; public E remove() { if(isempty()) throw new NoSuchElementException(); Node<E> rem = tail.next.next; tail.next.next = rem.next; if ( rem == tail ) tail = tail.next; return rem.item; public E poll() { return ( isempty() )? null : remove(); public E peek() { return ( isempty())? null : tail.next.next.item; 17
ADTs ADTs permitem programação modular: separam o programa em módulos mais pequenos; clientes diferentes podem partilhar a mesma ADT. ADTs permitem o encapsulamento: mantêm-se os módulos independentes; podem-se substituir as várias classes que implementam a mesma interface, sem alterar o cliente. Aspectos do desenho de uma ADT: especificação formal do problema; a implementação tem tendência a tornar-se obsoleta. 18
Lista duplamente ligada Inserção 19
Lista duplamente ligada Pesquisa 20
Lista duplamente ligada Remoção 21
Lista Duplamente Ligada public interface DList<E> extends Iterable<E>{ public int size(); public boolean isempty(); public void add(e e); public boolean remove(e e); 22
Iteradores public interface Iterable<E> { public Iterator<E> iterator(); public interface Iterator<E> { public boolean hasnext(); public E next(); public void remove(); 23
Lista Duplamente Ligada public class DLinkedList1<E> implements DList<E> { private static class DNode<T> { public DNode<T> next, prev; public T key; public DNode() { public DNode(T e ) { key=e; protected int size; protected DNode<E> head; public int size() { return size; public boolean isempty(){ return head == null; public void add(e e) { // Adiciona o elemento no inicio da lista ligada DNode<E> n=new DNode<E>(e); if(head!=null){ n.next=head; head.prev=n; head=n; //(continua...) 24
Lista Duplamente Ligada public class DLinkedList1<E> implements DList<E> { //(continuação...) // Remove o primeiro elemento da lista igual a e public boolean remove (E e){ DNode aux=search(e); if(aux!=null){ if(aux.prev!= null) {aux.prev.next = aux.next; else{head = aux.next; if(aux.next!= null){ aux.next.prev = aux.prev; return true; else return false; protected DNode<E> search( E e ){ DNode<E> aux = head; while( aux!= null &&!aux.key.equals(e)) aux=aux.next; return aux; //(continua...) 25
Lista Duplamente Ligada public class DLinkedList1<E> implements DList<E> { //(continuação...) public static <E> void show(dnode<e> l){ System.out.print("< "); while(l!= null){ System.out.print(l.key+" "); l = l.next; System.out.println(">"); public void reverse() { DNode<E> iter = head, aux; while( iter!= null ) { head = iter; aux = iter.next; iter.next = iter.prev; iter.prev = aux; iter = aux; //(continua...) 26
Lista Duplamente Ligada- Iteradores public class DLinkedList1<E> implements DList<E> { //(continuação...) private class LinkedIterator implements Iterator<E> { // Nó dos dados retornados pelo next() seguinte. protected DNode<E> node = head; /* Nó retornado pelo next() anterior. Como inicialmente não existiu next() referecia null. Depois de um remove() permite assinalar que não existe next() anterior.*/ protected DNode<E> prev = null; // Retorna true se existem mais elementos para iterar. public boolean hasnext() { return node!= null; // Retorna o elemento da posição corrente e avança. public E next() { if ( node == null )throw new NoSuchElementException(); prev = node; node = node.next; // Avança return prev.key; //(continua...) 27
Lista Duplamente Ligada- Iteradores public class DLinkedList1<E> implements DList<E> { //(continuação...) public void remove() { // Se a última operação não foi next() lança excepção if ( prev == null ) throw new IllegalStateException(); // Remove o nó do elemnto retornado pelo último next DLinkedList1.this.remove(prev); prev = null; // Assinala que a operação anterior não foi um next() //termina a classe LinkedIterator public Iterator<E> iterator() {return new LinkedIterator(); protected void remove( DNode<E> rem ){ if ( rem.next!= null) rem.next.prev=rem.prev; if ( rem.prev!= null ) rem.prev.next=rem.next; else head = rem.next; --size; //(...) 28
Lista com sentinela Inserção 29
Lista com sentinela Remoção 30
Lista com sentinela Pesquisa 31
Lista Duplamente Ligada public interface DCList<E>{ public int size(); public boolean isempty(); public void add(e e); public boolean remove(e e); 32
Lista Duplamente Ligada com Sentinela e Circular public class DLinkedList3<E> implements DCList<E>{ private static class DNode<E> { public DNode<E> next, prev; public E key; public DNode() { public DNode(E e ) { key=e; private int size; protected DNode<E> dummy; public DLinkedList3() { dummy=new DNode<E>(); dummy.next= dummy.prev = dummy; public int size() { return size; public boolean isempty() { return dummy.next == dummy; //(continua...) 33
Lista Duplamente Ligada com Sentinela e Circular public class DLinkedList3<E> implements DCList<E> { //(continuação...) public E getfirst() { return dummy.next.key; public E getlast() { return dummy.prev.key; // Adiciona o elemento no fim da lista ligada public void add(e e) { DNode<E> n=new DNode<E>(e); ++size; n.prev=dummy.prev; n.next=dummy; dummy.prev.next=n; dummy.prev=n; public boolean remove(e e){ DNode<E> aux=search(e); if(aux!=null){ aux.next.prev=aux.prev; aux.prev.next=aux.next; return true; return false; //(continua..) 34
Lista Duplamente Ligada com Sentinela e Circular public class DLinkedList3<E> implements DCList<E> { //(continuação...) public DNode<E> search(e e){ DNode<E> aux=dummy.next; while(aux!=dummy){ if(aux.key.equals(e)){ return aux; aux=aux.next; return null; //(continua...) 35
Lista Duplamente Ligada com Sentinela e Circular public class DLinkedList3<E> implements DCList<E> { //(...)/*Move os nós da lista 2 para a lista 1. Assume que as duas listas estão ordenadas *pela ordem dada pelo comparador. A lista 1 permanece ordenada e a lista 2 fica vazia.*/ public static < E > DNode<E> merge( DNode<E> dummy1, DNode<E> dummy2, Comparator<E> cmp) { DNode<E> head1 = dummy1.next; DNode<E> head2 = dummy2.next; while (head1!= dummy1 && head2!= dummy2 ) { if ( cmp.compare( head1.key, head2.key) <= 0 ) head1= head1.next; else { head2.prev = head1.prev; head1.prev.next = head2; head1.prev=head2; head2 = head2.next; head1.prev.next = head1; if ( head2!= dummy2 ) { head1.prev.next = head2; head2.prev = head1.prev; head1.prev = dummy2.prev; dummy2.prev.next = head1; dummy2.next = dummy2.prev = dummy2; return dummy1; 36
Lista Duplamente Ligada com Sentinela e Circular public class DLinkedList3<E> implements DList<E>{ //(...)/*Move os nós da lista 2 para a lista 1. Assume que as duas listas estão ordenadas *pela ordem dada pelo comparador. A lista 1 permanece ordenada e a lista 2 fica vazia.*/ public static < E > DNode<E> merge( DNode<E> dummy1, DNode<E> dummy2, Comparator<E> cmp) { DNode<E> head1 = dummy1.next; DNode<E> head2 = dummy2.next; while (head1!= dummy1 && head2!= dummy2 ) { if ( cmp.compare( head1.key, head2.key) <= 0 ) head1= head1.next; else { head2.prev = head1.prev; head1.prev.next = head2; head1.prev=head2; head2 = head2.next; head1.prev.next = head1; if ( head2!= dummy2 ) { head1.prev.next = head2; head2.prev = head1.prev; head1.prev = dummy2.prev; dummy2.prev.next = head1; dummy2.next = dummy2.prev = dummy2; return dummy1; 37
Exercício Implementar o algoritmo merge sort para duas listas duplamente ligadas. 38