Unidade 2: Filas de Prioridade Nesta Unidade vamos aprender: 1. O que é uma Fila de Prioridade 2. Motivação 3. O Tipo Abstrato de Dados: Fila de Prioridade 4. Implementação de uma Fila de Prioridade usando um Array 5.Implementação de uma Fila de Prioridade usando listas encadeadas 6. Heaps Binários: Uma estrutura especial para Filas de Prioridade 1. O que é uma Fila de Prioridade (FP)? - é uma fila na qual os elementos são inseridos em qualquer ordem, porém a remoção de um elemento é sempre feita por ordem de prioridade. Definição: Uma Fila de Prioridade é um Tipo Abstrato de Dados para armazenar uma coleção de elementos priorizados, com a seguinte particularidade: i) ii) Inserções podem ser feitas em qualquer lugar; Remoções são feitas considerando a ordem de prioridade. 2. Motivação Existem várias aplicações para Filas de Prioridade. Ex1.: Num aeroporto vários aviões estão aguardando ordem para pousar. Ao invés de se usar uma estrutura de Fila comum (do tipo FIFO), talvez seja mais conveniente usar uma FP onde a prioridade seria com relação a quantidade de combustível que resta em cada avião. Ex.2: Num Banco, a fila de atendimento ao caixa poderia ser única, porém as prioridades poderiam ser estabelecidas de acordo com a importância do cliente para o Banco (cliente especial). Importante: A prioridade pode ser baseada num atributo simples (ex. Tipo de conta: comum ou especial), ou baseada numa série de critérios (ex.: tipo de conta, idade, tempo de espera, etc) 3. O Tipo Abstrato de Dados Fila de Prioridade 3.1. O conceito de chave de busca Quando estudamos algoritmos de pesquisa, usamos o conceito de chave de busca que representa o(s) atributo(s) pelos quais as buscas serão realizadas. Numa FP a chave de busca é justamente a prioridade de um determinado elemento. Uma FP precisa realizar comparações entre as prioridades (ordem total). Suponha que k seja uma chave de busca, as seguintes propriedades devem ser obedecidas: - Reflexividade: k <= k - Assimetria: se k1 <= k2 e k2 <= k1, então k1 = k2 - Transitividade: se k1 <= k2 e k2 <= k3, então k1 <= k3. 3.2. Métodos de uma FP size(): retorna o número de elementos em uma FP. Input: nenhuma; Output: Integer isEmpty(): testa se a FP está vazia Input: nenhuma; Output: Boolean insertItem(e): insere um novo elemento Input: objeto e; Output: nenhuma findMin(): Retorna (mas não remove) o elemento da FP de menor chave (maior prioridade). Dá um erro se a FP estiver vazia. Input: nenhuma; Output: Objeto removeMin(): remove da FP e retorna o elemento de menor chave (maior prioridade). Dá um erro caso a FP esteja vazia. Input: nenhuma; Output: Objeto. Exercício: Escreva uma interface Java para o TAD FP. Solução: public interface FilaPrioridade { void inserir (Object e); Object removeMin() throws Exception; Object encontraMin() throws Exception; boolean ehVazio(); int tamanho(); } Exemplo: A tabela abaixo mostra uma série de operações sobre uma FP que está inicialmente vazia. Operação insere(5) insere(9) insere(7) encontraMin() removeMin() tamanho() removeMin() removeMin() removeMin() ehVazio() Output 5 5 2 7 9 “error” true Conteúdo FP {5} {5,9} {5, 7, 9} {5,7,9} {7,9} {7,9} {9} {} {} {} No caso de elementos compostos, podemos definir cada elemento como um par (k, e), onde k é a chave (prioridade) e e contém as informações do elemento em si (ex. nome, endereço, etc.) Uma possível definição para um item composto seria: public class Item { private Object chave, elem; protected Item (Object k, Object e) { chave = k; elem = e; } public Object getChave() { return chave; } public Object getElement() { return elem; } public void setChave(Object k) { chave = k; } public void setElement(Object e) { elem = e;} } Sobre as comparações das chaves, dependendo da aplicação a FP vai comparar vários tipos de dados. Por exemplo, uma FP pode ter como chave de prioridade a idade (integer), enquanto que uma outra FP pode ter como prioridade a distância entre dois pontos dispostos num plano. Solução 1: Escrever um método de comparação para cada novo tipo de aplicação! Solução 2: Escrever uma classe que realiza a comparação de objetos (genérica). Na solução 1, temos o problema de ter que estar escrevendo código parecido (ex. Comparação) para cada tipo de dado diferente que se queira comparar. Na solução 2, escrevemos o código de comparação apenas uma vez e usamos esta classe nas diversas FPs. Para a solução 2, podemos definir um TAD comparador com os seguintes métodos: ehMenorQue(a, b): Retorna true se a < b e false caso contrário. Input: par de objetos; Output: Boolean ehMenorOuIgualA(a, b): retorna true se a <= b e false caso contrário. Input: par de objetos; Output: Boolean ehIgualA(a,b): retorna true se a = b, false caso contrário. Input: par de objetos; Output: Boolean ehMaiorQue(a,b): retorna true if a > b, false caso contrário. Input: par de objetos; Output: Boolean ehMaiorOuIgualA(a,b): retorna true se a >= b e false caso contrário. Input: par de objetos; Output: Boolean ehComparavel(a): retorna true se a pode ser comparado, Input: Object; Output:Boolean Exercício: Implemente uma interface Java para o TAD Comparador. Exemplo de uso do Comparador: Comparação de pontos num plano de acordo com a ordem lexicográfica. public class Lexicographic implements Comparador { int xa, ya, xb, yb; // Campara pontos no plano com a // ordem lexicográfica. Assume que // um Point2D tem métodos getX() e // getY() que retornam suas // coordenadas private void getXY(Object a, Object b) { if (a == null || b == null) throw new InvalidElementException(“ Argumentos Nulos”); try { xa = ((Point2D a).getX(); ya = ((Point2D a).getY(); xb = ((Point2D b).getX(); yb = ((Point2D b).getY(); } catch (ClassCastException e) {throw new InvalidElementException(“Arg umento não é um Point2D”);} } public boolean ehMenorQue(Object a, Object b) { getXY(a,b); if (xa == xb) return (ya < yb); else return (xa < xb); } public boolean ehMenorOuIgualA (Object a, Object b) { getXY(a,b); if (xa == xb) return (ya <= yb); else return (xa <= xb); } public boolean ehIgualA(Object a, Object b) { getXY(a,b); return (xa == xb) && (ya == yb); } public boolean ehComparavel( Object a) { if (a == null) return false; else { try{ Point2D p = (Point2D) a; } catch (ClassCastException e){ return false; } return true; } } } 4. Implementação de uma Fila de Prioridade usando um Array Utilização de Arrays ordenados: Remoção: O primeiro elemento do array é o de maior prioridade Inserção: precisaremos inserir um elemento na sua ordem de prioridade no array (INEFICIENTE). Utilização de Arrays não-ordenados: Inserção: é simples pois precisamos apenas de adicionar o elemento no final do Array Remoção: temos que encontrar o elemento de maior prioridade, removê-lo e deslocar o último elemento do Array para a posição deixada pelo elemento removido. Ex.: Remoção do menor elemento num Array 30 50 30 50 30 50 10 40 20 40 20 40 20 Algoritmo removeMin() { int i; int maxItem; int maxIndex; if (!ehVazio()) { maxItem = FP[0]; maxIndex = 0; for (i=1; i < size(); i++) { if (FP[i] > maxItem) { maxItem = FP[i]; maxIndex = i; } } tamanhoFP --; FP[maxIndex] = FP[tamanhoFP]; return maxItem; } } 5.Implementação de uma Fila Prioridade usando listas encadeadas de Os elementos são ordenados em ordem decrescente de prioridade A operação de remoção é simples pois precisamos apenas remover o primeiro elemento da lista Na operação de inserção, nós temos que inserir o elemento na posição apropriada 1. Se a lista está vazia, inserir o elemento diretamente 2. Se o elemento a ser inserido possui prioridade maior ou igual ao primeiro elemento da lista, inserir o elemento no início da lista 3. Se o elemento a ser inserido tem prioridade menor do que o primeiro elemento da lista, proceder recursivamente até achar a posição apropriada para a inserção. Qual a limitação desta implementação?