truturas de Dados
Filas (Queues)
Prof. Leandro C. Fernandes
Estruturas de Dados
uras de Dados
Filas
Definição
Estrutura para armazenar um conjunto de elementos que funciona da seguinte forma:
Novos elementos sempre entram no “fim” da fila
O único elemento que se pode retirar da fila em um dado momento é o elemento do “início”
Para que serve
Para modelar situações em que é preciso “guardar para mais tarde” vários elementos e “lembrar” deles na mesma ordem em que foram armazenados.
F.I.F.O.
São também conhecidas “First In, First Out”
E0286 –Estruturas de Dados
Filas
Definição informal:
Uma fila, tal qual as pilhas, são estruturas lineares que:
diferem de uma pilha nas suas rotinas de inserção e remoção
um elemento pode ser inserido a qualquer instante, mas somente o elemento que está a mais tempo na fila pode ser removido
Característica:
Dada uma Lista L = (a
1, a
2, ..., a
n), dizemos que a
1é o elemento do início; a
né o elemento do fim, e a
i+1está depois de a
ina lista
a
1a
2a
3a
4a
5a
n-3a
n-2a
n-1a
n...
uras de Dados
Operações
Principais operações:
enqueue(Q, x): acrescenta um novo elemento x no final (cauda) da fila Q.
dequeue(Q): remove e retorna o elemento que está na frente (início) da fila Q.
Operações auxiliares:
front(Q): retorna o elemento da frente sem removê-lo.
size(Q): retorna o n
o. de elementos na fila Q.
isEmpty(Q): indica se a fila Q está vazia ou não.
E0286 –Estruturas de Dados
Aplicações de Filas
Aplicações Diretas:
Filas de espera.
Acesso a recursos compartilhados (p.ex. impressoras).
Buffers
Multi-programação (threads)
Aplicações Indiretas:
Componente de outras estruturas mais complexas.
Estrutura auxiliar para determinados algoritmos.
uras de Dados
Implementação Seqüencial
Podemos utilizar um vetor de tamanho N para armazenar os dados
um elemento para controlar o início, e
um elemento para controlar o fim
Com esse modelo conseguimos:
Primeiro elemento da fila: indicado por ini
Último elemento da fila: indicado por fim
Qtde de elementos na fila: fim – ini
E0286 –Estruturas de Dados
Implementação Seqüencial
0 1 2 3 4 16 17 18 19
...
ini fim qtde
0 0 0
0 1 2 3 4 16 17 18 19
...
ini fim qtde
0 1 1
0 1 2 3 4 16 17 18 19
...
ini fim qtde
0 19 19
0 1 2 3 4 16 17 18 19
...
ini fim qtde
3 19 16
0 1 2 3 4 16 17 18 19
...
ini fim qtde
3 20 17
uras de Dados
Implementação Seqüencial
Problema!?
Há espaço no vetor, porém não consigo utilizar ...
Solução:
Movimentar os dados
Péssimo ( :P ), pois consume tempo de execução
proporcional a quantidade de elementos da fila, i.e.,
de ordem O(n)
E0286 –Estruturas de Dados
Implementação Seqüencial
Use um vetor de tamanho N como uma estrutura circular:
Opera em ambos os extremos sem demandar deslocamentos!
0 1 2 3 4 16 17 18 19
...
ini fim qtde
3 19 16
0 1 2 3 4 16 17 18 19
...
ini fim qtde
3 20 17
0 1 2 3 4 16 17 18 19
...
ini fim qtde
3 21 18
uras de Dados
Implementação Seqüencial
Problema:
Como controlar isso?
Solução:
Explorar a propriedade do “resto da divisão”
Novo modelo de controle:
Primeiro elemento da fila: ini mod N
Último elemento da fila: fim mod N
Qtde de elementos na fila: (N + fim-ini) mod N
se 0
E0286 –Estruturas de Dados
Implementação Seqüencial
#define QUEUE_SIZE 10 typedef int tDado;
typedef struct Fila {
tDado dados[QUEUE_SIZE];
int ini;
int fim;
}
uras de Dados
Implementação Seqüencial
/* retorna o elemento do início da fila, sem removê-lo */
int front(Fila *Q, tDado *elem) { if (isEmpty(Q))
return 0;
else {
*elem = Q->dados[Q->ini];
return 1;
} }
/* retorna o múmero de elementos da fila */
int size(Fila *Q) {
return ((Q->fim - Q->ini) < 0)?
QUEUE_SIZE - (Q->ini - Q->fim) : Q->fim - Q->ini;
}
/* verifica se a fila está vazia */
int isEmpty(Fila *Q) {
return (size(Q) == 0)? 1:0;
E0286 –Estruturas de Dados
Implementação Seqüencial
/* insere no fim da fila */
int enqueue(Fila *Q, tDado elem) { if (isFull(Q))
return 0;
else {
Q->dados[Q->fim] = elem;
Q->fim = (Q->fim + 1)%QUEUE_SIZE;
return 1;
}
}
uras de Dados
Implementação Seqüencial
/* remove do início da fila */
int dequeue(Fila *Q, tDado *elem) { if (isEmpty(Q))
return 0;
else {
*elem = Q->dados[Q->ini];
Q->ini = (Q->ini + 1)%QUEUE_SIZE;
return 1;
}
}
E0286 –Estruturas de Dados
Desempenho e Limitação
Desempenho:
Seja n o número de elementos na fila
O espaço utilizado (memória) é O(N)
Cada operação roda em tempo O(1)
Limitação:
O tamanho máximo da fila deve ser definido a
priori e não pode ser alterado a não ser copiando
toda a fila para uma outra, de capacidade maior.
uras de Dados
Implementação Encadeada
Para eliminar a necessidade de prever o tamanho máx. da fila, utiliza-se uma implementação
dinâmica.
Encadeamento simples é suficiente:
a cabeça da lista é o início da fila, a cauda da lista é o fim da fila
O espaço utilizado (memória) é O(n)
ini fim
...
E0286 –Estruturas de Dados
Implementação Encadeada
typedef int tDado;
typedef struct elem * ptrElem;
typedef struct elem { tDado dado;
ptrElem prox;
};
typedef struct Fila { ptrElem ini;
ptrElem fim;
int qtdeElem;
}
uras de Dados
Implementação Encadeada
/* insere no fim da fila */
void enqueue(Fila *Q,tDado x) { ptrElem novo;
novo = (ptrElem) malloc(sizeof(Elem));
novo->dado = x;
novo->prox = NULL;
Q->fim->prox = novo;
Q->fim = novo;
Q->qtdeElem++;
}
ini fim
E0286 –Estruturas de Dados
ini fim
...
novo
Implementação Encadeada
/* insere no fim da fila */
void enqueue(Fila *Q,tDado x) { ptrElem novo;
novo = (ptrElem) malloc(sizeof(Elem));
novo->dado = x;
novo->prox = NULL;
Q->fim->prox = novo;
Q->fim = novo;
Q->qtdeElem++;
}
uras de Dados
ini fim novo
Implementação Encadeada
/* insere no fim da fila */
void enqueue(Fila *Q,tDado x) { ptrElem novo;
novo = (ptrElem) malloc(sizeof(Elem));
novo->dado = x;
novo->prox = NULL;
Q->fim->prox = novo;
Q->fim = novo;
Q->qtdeElem++;
}
E0286 –Estruturas de Dados
Implementação Encadeada
/* insere no fim da fila */
void enqueue(Fila *Q,tDado x) { ptrElem novo;
novo = (ptrElem) malloc(sizeof(Elem));
novo->dado = x;
novo->prox = NULL;
Q->fim->prox = novo;
Q->fim = novo;
Q->qtdeElem++;
}
ini fim
...
novo
uras de Dados
Seqüencial vs Dinâmica
Ambas realizam todas as operações em tempo O(1)
Implementação estática circular:
mais simples
Implementação dinâmica:
mais apropriada para filas cujo tamanho
não pode ser antecipado ou é muito variável
E0286 –Estruturas de Dados
Deques
(Double-Ended Queues)
Operações Básicas
inserir_inicio(D, x) inserir_fim(D, x) remover_inicio(D) remover_fim(D)
Operações auxiliares
primeiro(D) ultimo(D) size(D)
deque_vazio(D)
Estrutura que permite inserir e remover em ambos os extremos:
Inserção e Remoção no Início
Inserção e Remoção no Final
uras de Dados
Deques
Considerando que os deques requerem inserir e remover
elementos em ambos os extremos, implementações eficientes demandam:
Arranjo com estrutura circular, ou
Lista dinâmica duplamente encadeada
Nesses casos, todas as operações são executadas em tempo O(1)
O TAD Deque pode ser construído como uma via adaptação do TAD Lista:
A interface do TAD em questão (Deque) contém apenas suas
operações. A implementação, no entanto, utiliza as operações
do TAD adaptado (Lista)
E0286 –Estruturas de Dados
Exercícios
1.
Implementar uma biblioteca em C contendo todo o TAD Fila 1.a. Implementar via estrutura estática (arranjo circular)
1.b. Implementar utilizando as operações do TAD Lista
Projeto por adaptação
Vide implementação dinâmica de Pilha (aula anterior)
2.
Implementar uma biblioteca em C contendo todo o TAD Deque 2.a. Implementação dinâmica completa (duplamente encadeada) 2.b. Implementação via adaptação do TAD Lista
3.
Descreva as operações do TAD Fila em C utilizando as operações de Deque implementadas no exercício anterior
Projeto de Fila por adaptação de Deque
uras de Dados
Exercícios
1.
Repita o exercício anterior para o TAD Pilha.
2.
Implementar o TAD Fila via adaptação do TAD Pilha
Dica: Implemente a fila utilizando duas pilhas!
Apresente o tempo de execução de cada operação e discuta !
3.
Implemente um procedimento recursivo esvaziar(Q) que esvazie uma fila Q, a deixando como se tivesse sido reinicializada.
4.