Cap 5 - Pilhas Filas e Deques-4spp

Cap 5 - Pilhas Filas e Deques-4spp - Estruturas de Dados...

Info iconThis preview shows page 1. Sign up to view the full content.

View Full Document Right Arrow Icon
This is the end of the preview. Sign up to access the rest of the document.

Unformatted text preview: Estruturas de Dados Elementares ! M.T. Goodrich, R. Tamassia. Estruturas de Dados e Algoritmos em Java. 4a Edição. Ed. Bookman Pilhas, Filas e Deques Estruturas de Dados !  Cap. 3 2 Introdução ! Estruturas Elementares de Dados: Pilhas Pilhas (Stacks), !  Filas (Queues), !  Deques (Double-ended queues). Estruturas de Dados !  ! Estruturas Lineares: implementam alguma ordem linear entre seus elementos (existe a noção de ordem: primeiro elemento, último elemento, próximo elemento, anterior, sucessor). 3 O TAD PILHA objetos de um mesmo tipo (apesar de que pilhas reais podem armazenar objetos distintos). ! Inserções e Remoções seguem o esquema “lastin first-out” (o último a entrar é o primeiro a sair) ! Operações principais: !  !  push(objeto o): insere um elemento o no topo da pilha. pop(): remove e retorna o elemento que está no topo da pilha. Erro se pilha vazia. ! Aplicações diretas: ! Operações auxiliares: !  !  !  objeto top(): retorna (uma cópia do) último elemento inserido, se houver, sem retirá-lo da pilha. Erro se pilha vazia. inteiro size(): retorna o número de elementos armazenados na pilha. booleano isEmpty(): indica se a pilha está vazia (verdadeiro) ou não (falso). 5 !  !  !  Estruturas de Dados Estruturas de Dados ! Um TAD Pilha armazena Aplicações de Pilhas ! A Java Virtual Machine (JVM) !  !  Estrutura auxiliar para algoritmos (e.g. ordenação). Componente de outras estruturas de dados. 6 ! Uma forma simples de bar PC = 21 m=6 foo PC = 13 j=5 i=6 main PC = 2 i=5 7 Estruturas de Dados Estruturas de Dados ! Aplicações indiretas: Implementação Baseada em Vetores Pilha da JVM main() { int i = 5; mantém controle da cadeia dos métodos ativos através de uma foo(i); pilha. } ! Quando um método é chamado, a JVM insere na pilha foo(int j) { um registro com: int i; !  Variáveis locais e valor de retorno. i = j+1; !  Contador de programa que bar(i); mantém uma memória das declarações sendo executadas. } ! Quando um método encerra bar(int m) { execução, seu registro é removido da pilha e o controle … é passado ao método no topo } da pilha. Histórico de páginas visitadas em um navegador na Web. Controle de Desfazer/Refazer ações em um editor de textos. Encadeamento de chamadas a métodos ou funções em ambientes de execução como em C++ ou na Java Virtual Machine. implementar o TAD Pilha é usar vetores. ! Adicionamos elementos da esquerda para a direita. ! Uma variável t guarda informação sobre a posição do elemento que está no topo (o tamanho é t+1). S 012 Algoritmo pop(): se isEmpty() então ERRO_Pilha_Vazia senão t!t-1 retorne S[t + 1] Algoritmo push(o) se t = TAM_MAX - 1 então ERRO_Pilha_Cheia senão t!t+1 S[t] ! o … t 8 Implementação Java (parcial) baseada em Vetor A Interface Pilha (Stack) define quais operações serão implementadas, mas não como elas serão implementadas. ! A interface Stack a seguir equivale ao TAD Pilha. public interface Stack { public class ArrayStack implements Stack { public int size(); // vetor armazena os elementos private Object V[ ]; public boolean isEmpty(); public Object top(); public void push(Object o); public Object pop(); } "  Uma interface Java atua como uma espécie de “contrato”, indicando as operações que devem ser implementadas por toda classe que no futuro vier a implementá-la. 9 Estruturas de Dados Estruturas de Dados ! Uma interface em Java // posição do elemento do topo private int topo = -1; // pilha vazia // construtor public ArrayStack(int capacidade) { V = new Object[capacidade]; } !  !  Estruturas de Dados !  Seja n o número (máximo) de elementos na pilha. O espaço utilizado (de memória) é O(n). Cada operação custa tempo O(1). ! Limitações: !  !  O tamanho máximo da pilha deve ser definido a priori e não pode ser alterado a não ser criando uma nova pilha. Tentar inserir um novo elemento em uma pilha cheia produz uma exceção que deve ser tratada, e/ou uma mensagem de erro deve ser emitida. 11 } 10 Desempenho e Limitações ! Desempenho: public Object pop( ) { if isEmpty( ) return null; // pilha vazia Object temp = V[topo]; // facilita coleta de lixo V[topo] = null; topo = topo – 1; return temp; Filas O TAD Fila (Queue) ! Estruturas de Dados ! ! enqueue(objeto o): acrescenta um novo elemento o na cauda (fim) da fila. objeto dequeue(): remove e retorna o elemento que está na frente (início) da fila. !  !  ! Aplicações Diretas: ! Operações auxiliares: quaisquer (mas todos do mesmo tipo). Inserções e remoções devem seguir a política “First-in Firstout” (o primeiro a entrar é o primeiro a sair). Inserções são feitas sempre no FIM da fila, e as remoções sempre no INÍCIO da fila. ! Principais operações: objeto front(): retorna (uma cópia do) elemento do início da fila sem removê-lo. Erro se fila vazia. inteiro size(): retorna o número de elementos armazenados na fila. booleano isEmpty(): indica se a fila está vazia ou não. !  !  !  Exceções: Se a fila estiver vazia e houver uma tentativa de retirar um elemento (dequeue), então uma exceção deverá ocorrer EmptyQueueException. !  13 !  !  !  !  Estruturas de Dados ! O TAD Fila armazena objetos Aplicações de Filas Fila Implementada como Vetor Q c N-1 Configuração circular Q 012 c !  Estruturas de dados auxiliares para outros algoritmos. Componentes de outras estruturas mais complexas. 14 f 15 Algoritmo size() retorne N-f+c mod N básicas Estruturas de Dados Estruturas de Dados Configuração normal f !  ! Operações índice que aponta para o elemento da frente. índice que aponta para o elemento seguinte ao da cauda. 012 ! Aplicações Indiretas: Operações da Fila ! Use um vetor de tamanho N como uma estrutura circular. ! Ao menos duas variáveis devem ser mantidas: f c Filas de espera Acesso a recursos compartilhados (e.g., impressoras) Buffers Multiprogramação Algoritmo isEmpty() se (f = c) retorne true senão retorne false Q 012 f 012 c c N-1 Q f N-1 Aritmética Modular: x mod y = x – "x/y # * y (% em Java) 16 Operações da Fila (cont.) ! Estruturas de Dados ! Utilizamos o operador módulo (resto inteiro da divisão de inteiros). Operação enqueue lança uma exceção se o vetor já estiver “cheio” (N-1). Essa exceção não é definida no TAD pois depende da implementação. Garantir ao menos uma célula livre no vetor assegura que podemos checar se a fila está vazia verificando se c=f. Caso contrário, essa condição também ocorreria se a fila estivesse cheia. Q 012 f c Q 012 ! A operação dequeue Algoritmo dequeue() Algoritmo enqueue(o) se size() = N-1 então lançar FullQueueException senão Q[c] ! o c ! (c + 1) mod N c f 17 Estruturas de Dados ! Operações na Fila (cont.) Estruturas de Dados !  Para isso, aloque outro vetor com o dobro do tamanho (vide Cap. 5). Copie todos os elementos do vetor antigo para o novo vetor. Mude a referência do antigo vetor para o novo. O antigo vetor será “reciclado” (Coleta de Lixo). 19 Estruturas de Dados arranjo estiver cheio, ao invés de lançar uma exceção pode-se trocar o arranjo por outro maior: !  Q 012 f 012 c c Q ! Interface ! Durante a operação enqueue, quando o !  se isEmpty() então lançar EmptyQueueException senão o ! Q [f] Q[f] ! null f ! (f + 1) mod N retorne o f 18 A Interface Queue em Java Fila com Redimensionamento do Vetor !  lança uma exceção caso a fila esteja vazia. ! Essa exceção é declarada junto com a declaração do TAD fila. correspondente ao TAD Fila. ! Antes é preciso declarar a classe EmptyQueueException. public interface Queue { public int size(); public boolean isEmpty(); public Object front() throws EmptyQueueException; public void enqueue(Object o); public Object dequeue() throws EmptyQueueException; } Classe que também deve ser implementada 20 Implementação (parcial) baseada em Arranjo // construtor public ArrayQueue(int capacidade) { V = new Object[capacidade]; f = 0; c = 0; } public Object dequeue() throws EmptyQueueException{ if (isEmpty()) throw new EmptyQueueException ("Erro: Tentativa de retirar um elemento de uma fila vazia !"); else { Object temp = V[f]; V[f] = null; f = (f+1)%V.length; return temp; } } public class ArrayQueue implements Queue { // vetor que armazena os elementos private Object V[ ]; // posição do elemento da frente private int f; Estruturas de Dados // posição do elemento após a cauda private int c; public int size(){ return (V.length-f+c)%V.length; } public boolean isEmpty(){ return (f ==c); } // construtor default public ArrayQueue() { this(100); } } Listas Ligadas (Encadeadas) 21 A Classe Node para Listas Listas Encadeadas Simples ! Uma lista encadeada simples é !  Estruturas de Dados !  próximo elemento (objeto). referência (link) para o próximo nó. nó elemento $ A B C D 23 Estruturas de Dados ! uma estrutura de dados que consiste que uma seqüência interligada de nós. Cada nó armazena: public class Node { // Instance variables: private Object element; private Node next; /** Creates a node with null references to its element and next node. */ public Node() { this(null, null); } /** Creates a node with the given element and next node. */ public Node(Object e, Node n) { element = e; next = n; } // Accessor methods: public Object getElement() { return element; } public Node getNext() { return next; } // Modifier methods: public void setElement(Object newElem) { element = newElem; } public void setNext(Node newNext) { next = newNext; } } 24 Inserindo no Início da Lista Removendo do Início da Lista 1.  Atualizar head para 1.  Alocar o novo nó. 2.  Insirir o novo elemento. para o antigo head. 4.  Atualizar head para apontar para o novo nó (e.g. head = nd). PS 1. head é um apontador para o nó que aponta para o início da fila. PS 2. símbolo $ representa null. 25 Estruturas de Dados Estruturas de Dados 3.  Apontar o novo nó Inserindo no Final da Lista 26 Removendo do Final da Lista ! Remover no final de para null. 4.  Faça o antigo último nó apontar para o novo nó. 5.  Atualizar tail para apontar para o novo nó. PS 1. tail é um apontador assim como head. PS 2. não inverta a ordem dos passos (particularmente 4 e 5) !!! 27 Estruturas de Dados 1.  Alocar um novo nó. 2.  Insirir um elemento. 3.  Faça o novo nó apontar Estruturas de Dados apontar para o novo nó. 2.  Deixar o garbage collector recolher o antigo primeiro elemento da lista. uma lista encadeada simples não é eficiente!!! ! Não existe algoritmo de complexidade (tempo) constante para atualizar tail para apontar para o nó anterior. ! A remoção via recursão a partir de head custa O (n). 28 Implementação Pilha com uma Lista Ligada Simples public class LinkedStack implements Stack { private Node top; // reference to the head node private int size; // number of elements in the stack ! Podemos implementar uma pilha de forma eficiente public Object top() throws StackEmptyException { if (isEmpty()) throw new StackEmptyException ("Stack is empty."); return top.getElement(); public LinkedStack() { // initializes an empty stack com uma lista ligada simples. ! O elemento do topo é armazenado no início (1o. nó). ! O espaço utilizado é O(n) e cada operação do TAD pilha executa em tempo O(1). } top = null; size = 0; public Object pop() throws StackEmptyException { } if (isEmpty()) throw new StackEmptyException ("Stack is empty."); public int size() { return size; } public boolean isEmpty() { Object temp = top.getElement(); if (top == null) return true; Estruturas de Dados Estruturas de Dados top = top.getNext(); // link-out the former top node return false; nós size--; } t $ elementos 29 return temp; public void push(Object elem) { Node v = new Node(); // create a new node } } v.setElement(elem); v.setNext(top); // link-in the new node Porque inserimos e retiramos do início da lista ligada e não do final? top = v; size++; } 30 Implementação (parcial) public void enqueue(Object obj) { Fila com uma Lista Ligada Simples Node node = new Node(); node.setElement(obj); ! Podemos implementar uma fila de forma eficiente com uma lista ligada simples: !  Elemento da frente é armazenado no início (1o. nó). Elemento da cauda é armazenado no final (último nó). if (size == 0) head = node; // special case of a previously empty queue ! O espaço utilizado é O(n) e cada operação do TAD Estruturas de Dados fila executa com tempo O(1). r nós f Object obj; if (size == 0) throw new QueueEmptyException("Queue is empty."); obj = head.getElement(); head = head.getNext(); else tail.setNext(node); // add node at the tail of the list $ Estruturas de Dados !  node.setNext(null); // node will be new tail node public Object dequeue() throws QueueEmptyException { tail = node; // update the reference to the tail node size++; } elementos 31 size--; if (size == 0) tail = null; // head estará automaticamente apontando para null nesse caso. return obj; } PS. head e tail são referências para objetos do tipo Node. 32 Pilhas e Filas: Vetores x Listas Ligadas Deques ! Implementações baseadas em vetores: ! Utilizam listas duplamente encadeadas porque requerem Estruturas de Dados !  Vantagens: Simplicidade e complexidade constante O(1) para efetuar qualquer ação. Desvantagem: Tamanho limitado definido a priori. !  ! insertFirst(o): Entrada: objeto; Saída: nenhuma. ! Implementações baseadas em listas: !  inserção e remoção (eficiente) de elementos em ambos os extremos da lista. Implementam filas e pilhas por adaptação. No caso de Java implementa-se a interface do TAD correspondente e declara-se apenas os seus métodos, utilizando os métodos correspondentes do Deque. Vantagens: Tamanho variável e complexidade O(1) para efetuar operação básica. Desvantagem: Complexidade de implementação um pouco maior, que requer cuidados adicionais. 33 insertLast(o): Entrada: objeto; Saída: nenhuma. Estruturas de Dados !  Lista Duplamente Encadeada removeFirst(): Entrada: nenhuma; Saída: objeto. TAD: removeLast(): Entrada: nenhuma; Saída: objeto. first(): Entrada: nenhuma; Saída: objeto. last(): Entrada: nenhuma; Saída: objeto. size(): Entrada: nenhuma; Saída: inteiro. isEmpty(): Entrada: nenhuma; Saída: booleano. 34 Implementação de Node public class DLNode { private Object element; Omitido que nós (sentinelas) header e trailer contêm null e estão encadeados com null. header nós principais private DLNode next, prev; trailer DLNode() { this(null, null, null); } DLNode(Object e, DLNode p, DLNode n) { element = e; elementos Vantagem: Inserir ou remover elementos em qualquer extremo da lista pode ser feito em tempo constante O(1). Na lista simples remover elemento do final custa tempo O(n) pois não temos qualquer referência para o penúltimo objeto a não ser buscando a partir do início. 35 Estruturas de Dados Estruturas de Dados next = n; prev = p; } public void setElement(Object newElem) { element = newElem; } public void setNext(DLNode newNext) { next = newNext; } public void setPrev(DLNode newPrev) { prev = newPrev; } public Object getElement() { return element; } public DLNode getNext() { return next; } public DLNode getPrev() { return prev; } } 36 ...
View Full Document

This note was uploaded on 04/02/2012 for the course DC 11 taught by Professor Milton during the Spring '12 term at Alaska Pacific University.

Ask a homework question - tutors are online