AED Algoritmos e Estruturas de Dados LEEC - 2004/2005 Filas com Prioridade Filas com prioridade - Introdução (1) Filas com prioridade (Priority Queues) usadas em diversas aplicações: Despacho (scheduler) de tarefas. Compressão de ficheiros. Algoritmos de ordenação. Operações mais usuais em filas com prioridade: Criar fila, a partir de um conjunto de items Inserir novo item Remover o item com prioridade mais elevada Remover um item Modificar a prioridade de um item Juntar duas filas de prioridade AED (IST/DEEC) 2
Filas com prioridade - Introdução (2) Interface do tipo abstracto: void PQinit(int); /* inicializa fila com prioridade, com número máximo de items */ BOOL PQempty(); /* testa fila vazia */ void PQinsert(Item); /* insere item */ Item PQdelmax(); /* elimina item de maior prioridade */ as filas de prioridade, todos os elemento possuem uma chave, que pode ser usada para ordenar a fila. AED (IST/DEEC) 3 Filas com prioridade por tabela (1) Implementação por tabela não ordenada #include <stdlib.h> #include Item.h /* aqui o cliente define os dados */ static Item *queue; static int free; /* primeira posição livre*/ void PQinit(unsigned Size) { PQinsert(i1); PQinsert(i3); PQinsert(i2); queue = (Item *) malloc(size * sizeof(item)); free = 0; } void PQinsert(Item I) { /*elemento inserido sempre na primeira posição livre*/ queue[free++] = I; } BOOL IsEmpty() { return free == 0? TRUE : FALSE; } free=3 i2 i3 i1 AED (IST/DEEC) 4
Filas com prioridade por tabela (2) Item PQdelmax() { /* elimina elemento de maior chave */ int Idx, Max; /* procura elemento com maior chave */ for(idx=1, Max=0; Idx<free; Idx++) if(less(queue[max], queue[idx])) Max = Idx; exch(queue[max], queue[free-1]); /* troca topo com o elemento de maior chave */ return(queue[--free]); } Seja Key(i3)>Key(i2)>Key(i1) PQdelmax() dá: i2 i3 i2 free=3 i1 free=2 i1 AED (IST/DEEC) 5 Filas com prioridade por tabela (3) Tabela ordenada pela chave Inserção e remoção de um elemento com prioridade qualquer, exige varrimento da tabela para deslocar items (no pior caso, toda a tabela). Procura e remoção do elemento com maior chave em tempo constante (acesso imediato ao topo). A implementação das filas de prioridade por acervos (heaps) permite o melhor equilíbrio no desempenho da procura (e remoção) de elementos com maior e com prioridade qualquer. AED (IST/DEEC) 6
Filas com prioridade por tabela (4) Complexidade, no pior caso, das operações das filas com prioridade (segundo algumas implementações) Inserção Remover chave máxima Remover Encontrar chave máxima Modificar prioridade Junção Tabela ordenada 1 1 Tabela não ordenada 1 1 1 Acervo (Heap) lg lg lg 1 lg AED (IST/DEEC) 7 Acervos (1) Definição Um acervo (heap) é uma representação, em tabela, de uma árvore que satisfaz a condição de acervo (heap condition) : o valor da chave associada a cada nó é superior ao valor das chaves associadas em todos os nós de altura inferior A ordenação dos acervos é parcial (heap-ordering) Vantagens da representação de árvores por tabela Menor ocupação de memória Acesso constante a qualquer elemento Inconvenientes da representação de árvores por tabela ecessário conhecer tamanho à partida AED (IST/DEEC) 8
Acervos (2) Convenção: nos acervos, a prioridade é maior quanto menor for o número. #define lesspri(a, B) (key(a) > key(b)) A criação de um acervo, a partir de uma árvore, é feita percorrendo a árvore em largura (breadht-first). Se árvore não for completa, existem casas vazias: a solução é impor que todos os nós intermédios sejam preenchidos. AED (IST/DEEC) 9 Acervos (3) Exemplo A1: Acervo de 12 posições 3 3 5 7 9 12 16 20 25 22 28 32 40 9 5 12 16 7 20 O nó ascendente da posição i é (i-1)/2 Exemplo: nó ascendente da posição 6 (prioridade 20), está na posição (6-1)/2 = 2 (prioridade 7) Os nós descendentes da posição i são 2i+1 e 2(i+1) 25 22 28 32 40 Exemplo: nós descendentes da posição 0 (prioridade 3), estão nas posições 1 (prioridade 5) e 2 (prioridade 7) Exemplo: nós descendentes da posição 2 (prioridade 7), estão nas posições 5 (prioridade 16) e 6 (prioridade 20) ota! o livro do Sedgewick, os índices da tabela são considerados a partir de 1, Aqui usamos a convenção do C (a partir de 0) AED (IST/DEEC) 10
Acervos (4) Alteração/inserção/remoção de um elemento pode quebrar a condição dos acervos. Exemplo: se a chave for decrementada (valor substituído por outro maior), os descendentes podem passar a ter chaves maiores. Se a chave for decrementada, verificar nós de alturas inferiores descendo o caminho até à folha Se a chave for incrementada, verificar nós de alturas superiores subindo o caminho até à raiz AED (IST/DEEC) 11 Síntese da Aula 1 de Filas com Prioridade Introdução às filas com prioridade Operações abstractas Implementação por tabelas não-ordenadas Introdução aos acervos (heaps) AED (IST/DEEC) 12
Acervos (5) Reposição da ordenação num acervo A. Se prioridade de um nó for aumentada, enquanto não for restabelecida a condição de acervo ou atingida a raiz, trocar com o nó ascendente. void FixUp(Item Heap[], int Idx) { while (Idx > 0 && lesspri(heap[(idx-1)/2], Heap[Idx])) { exch(heap[idx], Heap[(Idx-1)/2]); Idx = (Idx-1)/2; } } Fácil verificar que FixUp é O(lg ) AED (IST/DEEC) 13 Acervos (6) Exemplo A2: o exemplo A1, nó de prioridade 28 (posição 9) passa a ter prioridade 4 3 3 5 7 9 12 10 20 25 22 4 32 40 5 7 9 12 10 20 Heap[9]=4, Heap[(9-1)/2]=12 25 22 4 32 40 lesspri(heap[4], Heap[9]): trocar e chamar FixUp sobre a posição 4 AED (IST/DEEC) 14
Acervos (7) 3 3 5 7 9 4 10 20 25 22 12 32 40 5 7 9 4 10 20 Heap[4]=4, Heap[(4-1)/2]=5 25 22 12 32 40 lesspri(heap[1], Heap[4]): trocar e chamar FixUp sobre a posição 1 AED (IST/DEEC) 15 Acervos (8) 3 3 4 7 9 5 10 20 25 22 12 32 40 4 7 9 5 10 20 Heap[1]=4, Heap[(1-1)/2]=3 25 22 12 32 40!lessPri(Heap[0], Heap[1]): condição de acervo satisfeita!! AED (IST/DEEC) 16
Acervos (9) B. Se prioridade de um nó for diminuída, enquanto não restabelecer a condição de acervo ou atingir uma folha, trocar com o maior descendente. void FixDown(Item Heap[], int Idx, int ) { int Child; /* índice de um nó descendente */ while(2*idx < ) { /* enquanto não chegar às folhas */ Child = 2*Idx+1; /* selecciona o maior descendente. ota: se índice Child é -1, então só há um descendente */ if (Child < && lesspri(heap[child], Heap[Child+1])) Child++; if (!lesspri(heap[idx], Heap[Child])) break; /*condição acervo satisfeita */ exch(heap[idx], Heap[Child]); /* continua a descer a árvore */ Idx = Child; }} Fácil verificar que FixDown é O(lg ) AED (IST/DEEC) 17 Acervos (10) Exemplo A2: o exemplo A1, nó de prioridade 3 (posição 0) passa a ter prioridade 14 14 14 5 7 9 12 10 20 25 22 28 32 40 5 7 9 12 10 20 Idx=0, Child=2*0+1=1 25 22 28 32 40!lessPri(Heap[1],Heap[2]): foca na subárvore esq. lesspri(heap[0],heap[1]): troca e chama FixDown sobre a posição 1 AED (IST/DEEC) 18
Acervos (11) 5 5 14 7 9 12 10 20 25 22 28 32 40 14 7 9 12 10 20 Idx=1, Child=2*1+1=3 25 22 28 32 40!lessPri(Heap[3],Heap[4]): foca na subárvore esq. lesspri(heap[1],heap[3]): troca e chama FixDown sobre a posição 3 AED (IST/DEEC) 19 Acervos (12) 5 5 9 7 14 12 10 20 25 22 28 32 40 9 7 14 12 10 20 Idx=3, Child=3*1+1=7 25 22 28 32 40 lesspri(heap[7],heap[8]): foca na subárvore dir.!lesspri(heap[3],heap[8]): condição de acervo satisfeita AED (IST/DEEC) 20
Filas com prioridade por acervo (1) #include Item.h /* aqui o cliente define os dados */ static Item *queue; static int free; /* número de elementos existentes no acervo */ void PQinit(unsigned Size) { queue = (Item *)malloc(size*sizeof(item)); free = 0; } void PQinsert(Item I) { /* insere novo elemento no fim e restabelece ordenação com FixUp */ queue[free] = I; FixUp(queue, free); free++; } AED (IST/DEEC) 21 Filas com prioridade por acervo (2) Item PQdelmax() { /* troca maior elemento com último da tabela e reordena com FixDown */ exch(queue[0], queue[free-1]); FixDown(queue, 0, free-2); /* ultimo elemento não considerado na reordenação */ return queue[--free]; } AED (IST/DEEC) 22
Ordenação por acervo (1) O algoritmo de ordenação, usando uma fila com prioridade, é muito simples: 1. Inserir a tabela numa fila com prioridade, implementada por acervo. 2. Chamar PQdelmax() para identificar a tabela inversamente ordenada. void PQsort(Item ptable[], int L, int R) { int Aux; PQinit(R-L+1); for(aux = L; Aux <= R; Aux++) PQinsert(pTable[Aux]); for(aux = R; Aux >= L; Aux--) ptable[aux] = PQdelmax(); } AED (IST/DEEC) 23 Ordenação por acervo (2) Complexidade do processamento: melhor que lg Inicialização: O(1) Inserção: lg 1 + lg 2 +... + lg (< lg ) Retirada dos elementos do acervo: lg +... + lg 2 + lg 1 (< lg ) Complexidade da memória extra:, para o acervo A memória extra pode ser eliminada, se o acervo for construído na própria tabela a ordenar. Primeiro, constroi-se o acervo chamado FixDown na metade inicial da tabela. Depois, troca-se a raiz com o último elemento e faz-se o FixDown sobre a tabela de dimensão menos 1) AED (IST/DEEC) 24
Ordenação por acervo (3) void Heapsort(Item Table[], int L, int R) { int Aux, Top = R; /* Constroi acervo na propria tabela, exercendo FixDown na parte inferior */ for(aux = L+(R-L)/2; Aux >= L; Aux--) FixDown(Table, Aux, R); } /* Reordena a tabela, trocando o topo e exercendo FixDown na tabela com dimensão 1 ( na troca, o menor é já colocado na posição final) */ while(top > L){ exch(table[l], Table[Top]); FixDown(Table, L, --Top); } AED (IST/DEEC) 25 Ordenação por acervo (4) Análise da complexidade Construção do acervo custa O() Os k maiores elementos (K<<) podem ser identificados em O(), bastando interromper o 2º ciclo ao fim de k iterações. Ordenação custa sempre O( lg ), qualquer que seja o formato da entrada ver exemplos no Sedgewick.5 AED (IST/DEEC) 26
Síntese da Aula 2 de Filas com Prioridade Reposição nos acervos Ascendente Descendente Implementação de filas, com prioridade, por acervos Ordenação por acervo AED (IST/DEEC) 27