Buscar

Estrutura de dados II

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 26 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 26 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 26 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

Estrutura de Dados 
Listas Lineares
Material Teórico
Responsável pelo Conteúdo:
Prof. Ms Amilton Souza Martha
Revisão Textual:
Profa. Esp.Vera Lídia de Sá Cicarone
5
• Introdução
• Pilhas
• Controle de Chamadas e Retornos de Rotinas
 
 Atenção Atenção
Para um bom aproveitamento do curso, leia o material teórico atentamente antes de realizar as 
atividades. É importante também respeitar os prazos estabelecidos no cronograma.
 · Nesta unidade, vamos trabalhar com estruturas de dados 
lineares e implementá-las utilizando alocação dinâmica 
encadeada baseada em listas ligadas.
 · Os programas já estão prontos no material, mas é muito 
importante que o aluno escreva os códigos e faça vários 
testes. Não basta apenas entender os códigos; mas é preciso 
saber utilizá-los.
Listas Lineares
• Avaliação de Expressões
• Filas
6
Unidade: Listas Lineares
Contextualização
Muitos problemas computacionais possuem soluções algorítmicas semelhantes. Por exemplo, 
um spool de impressão, atendimentos de um caixa de banco ou a lista de e-mails pendentes em 
uma caixa de saída são problemas em que a ordem de entrada dos elementos é a mesma ordem 
de saída, ou seja, uma aplicação típica de FILA.
Assim também abrir menus e submenus, criar uma pilha de produtos em um supermercado 
ou simular uma torre de Hanói são algoritmos em que a ordem de entrada é o contrário da 
ordem de saída, ou seja, aplicação típica de PILHA.
Portanto, faz-se necessário entender como essas estruturas funcionam, como usar a 
implementação dos algoritmos para servir como ferramenta na hora em que um problema 
exigir uma solução semelhante.
Não só entender a parte teórica, mas também saber implementar, utilizando um Tipo Abstrato 
de Dados, em uma linguagem POO, como Java, e um tipo de alocação de memória estudado 
é fundamental para poder usar o conteúdo aprendido.
7
Introdução
Uma lista linear é um tipo abstrato de dado (TAD) que representa uma coleção L:[a1,a2,...,an], 
n>=0 cuja propriedade estrutural baseia-se apenas na posição relativa dos elementos, que são 
dispostos linearmente. Se n=0, dizemos que a lista L é vazia; caso contrário, são válidas as 
seguintes propriedades:
 » a1 é o primeiro elemento da lista L;
 » an é o último elemento da lista L;
 » ak, 1<k<n, é precedido pelo elemento ak-1 e seguido por ak+1 na lista L.
Ou seja, a característica fundamental de uma lista linear é o sentido de ordem unidimensional 
dos elementos que a compõem; uma ordem que nos permite dizer com precisão onde a coleção 
inicia e onde termina, sem possibilidade de dúvida. Temos alguns casos especiais de Listas 
Lineares que são frequentemente utilizadas na modelagem de problemas a serem resolvidos 
computacionalmente. Elas são: Pilhas, Filas e Listas Ordenadas.
Para podermos implementar as listas lineares, devemos avaliar como armazenaremos os 
elementos da lista na memória do computador. A alocação de memória, do ponto de vista do 
programador, pode ser classificada em duas categorias. A primeira refere-se à quantidade de 
memória usada para armazenar os dados na estrutura; nesse caso a alocação pode ser Estática 
ou Dinâmica. A segunda refere-se à forma como os elementos estão alocados em relação ao 
seu sucessor; aqui, a alocação pode ser Sequencial ou Encadeada.
Dizemos que a alocação é estática quando, durante toda a execução, a quantidade de 
memória utilizada pelo programa não varia, ou seja, a área de memória alocada já foi prevista no 
código do programa. Por outro lado, se o programa for capaz de criar novas variáveis enquanto 
executa, isto é, se áreas de memória que não foram declaradas no programa passarem a existir 
durante a sua execução, então, dizemos que a alocação é dinâmica. 
Uma vantagem da alocação estática é que a maioria das linguagens de programação possui 
esse recurso embutido na criação de arrays. Veja, a seguir, como criar um vetor de inteiros com 
tamanho fixo de 50 posições em Java:
• int[] x = new int[50];
Note que o vetor vai ocupar 50 posições de memória, mesmo que não seja preenchido totalmente, 
o que é uma desvantagem da alocação estática, pois podemos ter superdimensionamento ou 
subdimensionamento da estrutura. Já, na alocação dinâmica, podemos solicitar mais memória 
à medida que ela for necessária durante a execução do programa ou devolvê-la quando não for 
mais necessária, porém requer implementação.
A alocação sequencial consiste em dispor os elementos de uma lista em células de memória 
consecutivas, uma após a outra. Assim, se um elemento está na posição de memória “&” e 
cada elemento possui o mesmo tipo e ocupa “k” bytes, o elemento posterior da lista estará na 
posição (&+k) e o anterior em (&-k). Essa é uma grande vantagem da alocação sequencial, 
pois, dado o endereço inicial & e o índice i de um elemento qualquer da lista, podemos acessá-
lo imediatamente, com um simples cálculo. O ponto fraco deste tipo de alocação aparece 
8
Unidade: Listas Lineares
quando precisamos inserir ou excluir elementos no meio de uma lista, o que torna o processo 
mais complicado, pois devemos mover os elementos de modo a abrir um espaço para inserção 
ou ocupar um espaço de um elemento excluído. Na Figura 1, podemos ver um exemplo de 
alocação sequencial. Note que os elementos estão fisicamente na sequência.
Figura 1 – Alocação Sequencial de Memória
 Porém, na alocação encadeada, os elementos não precisam, necessariamente, ocupar 
posições consecutivas de memória, ou seja, podem ocupar quaisquer células. Contudo, para 
conseguir manter uma ordem, cada elemento da lista carrega consigo a posição de memória 
do próximo elemento da lista. A grande vantagem na alocação encadeada é a facilidade 
de inserção e remoção de elementos, pois não precisamos movimentar os dados, somente 
precisamos atualizar o campo de ligação do elemento antecessor àquele que será inserido ou 
removido. A desvantagem surge quando desejamos acessar uma posição específica da lista, já 
que conhecemos inicialmente somente o endereço do primeiro elemento e precisamos varrer a 
lista até encontrar o elemento desejado. É claro que, para listas muito extensas, esta operação 
pode ser bastante desvantajosa em relação ao tempo. 
Veja, na Figura 2, que cada elemento aponta para o próximo elemento, compondo uma lista 
encadeada ou, também chamada, lista ligada.
Figura 2 – Alocação Encadeada de Memória
 Em teoria, podemos combinar os 2 tipos de locação de memória tendo as seguintes combinações:
 » Alocação Estática Sequencial
 » Alocação Estática Encadeada
 » Alocação Dinâmica Sequencial
 » Alocação Dinâmica Encadeada
Porém, na prática, a alocação Estática Sequencial é muito utilizada, baseando-se em Tipos 
Estruturados como vetores, já que são pré-alocados e disponíveis na maioria das linguagens. A 
alocação Estática Encadeada é inviável, já que tem a desvantagem do desperdício de memória 
e tempo de busca. A alocação Dinâmica Sequencial não é possível, já que devemos solicitar 
memória dinamicamente e não garantimos a sequência de memória disponível, e a alocação 
Dinâmica Encadeada é o melhor modelo para implementação.
9
Pilhas
 A Pilha é um tipo especial de lista linear em que todas as inserções e remoções são feitas em 
uma mesma extremidade denominada topo. Então, temos acesso somente ao topo da pilha, ou 
seja, quando queremos inserir um elemento na pilha, colocamos no topo e, se quisermos excluir 
um elemento da pilha, só podemos excluir aquele que está no topo.
Esses tipos de listas em que a inserção e remoção são feitas por uma única extremidade são 
conhecidas como listas LIFO (Last-In/First-Out) - último elemento que entra é o primeiro que sai.
Uma Pilha possui três operações básicas:
 » TOP: acessa o elemento posicionado no topo da pilha e retorna;
 » PUSH: insere um novo elemento no topo da pilha;
 » POP: remove um elemento do topo da pilha e retorna;
Veja na Tabela 1, a seguir, um exemplo de manipulação de Pilhas
Tabela 1 – Exemplo de Manipulação de Pilha
--------P:[ ] Inicialmente a Pilha está Vazia.
push(a) P:[a] Insere o elemento a na Pilha P.
push(b) P:[a, b] Insere o elemento b na Pilha P. Note que, agora, o topo é b.
push(c) P:[a, b, c] Insere o elemento c na Pilha P. Agora o topo contém c.
pop() P:[a,b] Remove o elemento do topo e retorna o conteúdo. No caso c.
push(d) P:[a, b, d] Insere o elemento d no topo..
top() P:[a, b, d] Não altera a Pilha, apenas retorna o valor do topo da Pilha (d).
push(top()) P:[a, b, d, d] Insere no topo mais uma cópia do topo.
push(pop()) P:[a, b, d, d] Insere no topo o que foi excluído do topo (fica do mesmo jeito).
Para a implementação de uma Pilha, podemos escolher dois modelos de alocação de 
memória: a Estática-Sequencial ou a Dinâmica-Encadeada.
Vamos começar mostrando uma implementação mais simples com alocação Estática-
Sequencial usando a linguagem Java, porém o conceito pode ser utilizado em qualquer outra 
linguagem de programação.
10
Unidade: Listas Lineares
Figura 3 – Trecho de uma Pilha Estática Sequencial
 Na Figura 3, podemos ver um trecho de implementação de uma Pilha Estática Sequencial. 
Podemos notar que os elementos da pilha serão armazenados em um vetor de Objects com 
tamanho fixo de 30 posições. 
Um problema encontrado nas Pilhas Estáticas é a limitação da inserção de elementos na Pilha, 
pois ela estava limitada pelo tamanho do vetor declarado. A Pilha dinâmica faz uma requisição à 
heap (área de memória disponível no sistema) toda vez que um novo elemento precisa ser inserido 
na Pilha assim como desaloca a memória usada por um elemento que foi excluído dela. Com isso, 
usamos somente a quantidade de memória exata de que o programa necessita. 
Portanto, vamos implementar apenas uma Pilha Dinâmica Encadeada e, para isso, vamos 
imaginar que cada elemento da Pilha será um nó, em que teremos acesso somente ao topo 
(estrutura LIFO). Cada elemento deverá conter o endereço do próximo elemento da Pilha. 
Chamamos de Lista Simplesmente Encadeada, pois o elemento só conhece o endereço do 
seu próximo e não do seu anterior. Se fosse necessário armazenar também o endereço do nó 
anterior, teríamos uma Lista Duplamente Encadeada.
Veja, na Figura 4, a seguir, a implementação de um tipo abstrato para o nó de uma lista 
simplesmente encadeada, que servirá para nossa Pilha Dinâmica. Veja que, além do dado que 
será armazenado, temos um campo para guardar a referência do próximo nó (prox).
Figura 4 – Nó de uma Lista Simplesmente Encadeada
 Como, em uma Pilha, só manipulamos uma das extremidades denominada topo, precisamos 
ter a referência somente de um nó. Veja, a seguir, na Figura 5, a implementação de uma Pilha 
Dinâmica Encadeada.
11
 Figura 5 – Pilha Dinâmica Encadeada
 
 
Aplicação de Pilhas
Qualquer aplicação em que a ordem de entrada dos elementos é contrária à ordem de saída 
é uma candidata ao uso de Pilhas para implementação de seus elementos. Vamos ver, agora, 
alguns exemplos de uso de pilhas na computação.
12
Unidade: Listas Lineares
Controle de Chamadas e Retornos de Rotinas
Tanto na programação estruturada como na orientada a objetos, as funções ou métodos 
são técnicas bastante utilizadas para o reaproveitamento de algoritmos implementados. Essas 
funções são sub-rotinas ou subprogramas dentro de seu programa principal.
Para formar os programas, as rotinas são conectadas entre si através de um mecanismo 
de chamada e retornos, e as pilhas desempenham um papel fundamental no seu controle. 
Considere uma linguagem hipotética com as seguintes instruções: 
 » print - imprime mensagem no vídeo 
 » call - chama a sub-rotina 
 » return - retorna à sub-rotina que chamou 
Veja o programa a seguir dividido em 3 sub-rotinas (rotina A, rotina B e rotina C). A execução 
começa pela rotina A e o controle é passado às sub-rotinas e retornado de onde parou na rotina 
que chamou até encerrar na última linha da rotina A. 
Rotina A Rotina B Rotina C
1 - Print “Um” 5 - Call C 8 - Print “Dois
2 - Call B 6 - Print “Três” 9 - Return
3 - Call C 7 - Return
4 - Return
Cada vez que chamamos uma sub-rotina, o sistema operacional precisa guardar o endereço 
de retorno, continuando de onde parou quando o controle foi transferido. Nesse caso, os 
endereços de retorno são empilhados. Veja a pilha de controle na Tabela 2 a seguir:
Tabela 2 – Controle de Chamadas usando Pilhas
Instrução Pilha de Controle
-------- P:[ ]
1 - print “Um” P:[ 0 ]
2 - call B P:[ 0, 3 ]
5 - call C P:[ 0, 3, 6 ]
8 - print “Dois” P:[ 0, 3, 6 ]
9 – return P:[ 0, 3 ]
6 - print “Três” P:[ 0, 3 ]
13
7 – return P:[ 0 ]
3 - call C P:[ 0, 4 ]
8 - print “Dois” P:[ 0, 4 ]
9 – return P:[ 0 ]
4 – return P:[ ]
Podemos notar, na Tabela 2, que a pilha começa com 0, que simboliza o programa sendo 
executado na memória. A cada chamada de sub-rotina (comando call), o próximo endereço é 
armazenado na pilha para que seja executado quando a sub-rotina for encerrada (comando 
return). Ao final do processo, a pilha de controle deverá estar vazia, simbolizando que o programa 
encerrou com sucesso.
Caso haja uma falha na execução do programa no meio do caminho, a pilha de memória 
não é esvaziada normalmente, ocorrendo erros como “Este programa executou uma operação 
ilegal e será finalizado ...”. E, nesse caso, o sistema operacional vai esvaziar a pilha de controle.
Avaliação de Expressões
Estamos tão acostumados a digitar expressões e receber resultados que raramente paramos 
para pensar sobre o que deve acontecer dentro do computador a partir do momento em que 
fornecemos uma expressão até o momento em que recebemos a resposta. 
As operações são efetuadas de acordo com a ordem determinada pelas regras usuais 
da matemática (prioridades), ou seja, em primeiro lugar as multiplicações e divisões e, 
posteriormente, as somas e subtrações, sendo que os parênteses podem alterar a ordem de 
prioridade entre as operações.
Existem duas dificuldades ao avaliar uma expressão:
1 - A existência de prioridades diferentes para os operadores, que não nos permite efetuá-los 
na ordem em que encontramos na expressão;
2 - A existência de parênteses, que alteram a prioridade das operações.
Felizmente, um lógico polonês chamado Jan Lukasiewicz conseguiu criar uma representação 
para expressões em que não existem prioridades e nem a necessidade do uso dos parênteses.
Habitualmente, nós usamos a representação na qual o operador fica no meio dos operandos, 
mas existem outras possibilidades.
 » Infixa: operador aparece entre os operandos ( A + B )
 » Pré-Fixa: operador aparece antes dos operandos ( +AB )
14
Unidade: Listas Lineares
 » Pós-Fixa: operador aparece após os operandos ( AB + )
A forma pré-fixa é chamada Notação Polonesa e a pós-fixa, Notação Polonesa Reversa 
(NPR). A Notação Polonesa Reversa tem se mostrado mais eficiente no uso para a avaliação de 
expressões e estudaremos mais profundamente esse caso. 
A vantagem do uso dessa notação é que, como o operador aparece imediatamente após os 
operandos que deve operar, a ordem em que aparecem é a ordem em que deve ser efetuada a 
operação, sendo desnecessário o uso de parênteses ou regras de prioridade. Veja, na Tabela 3, 
alguns exemplos de conversão da notação infixa para notação polonesa reversa.
 
Tabela 3 – Exemplos de Conversão para NPR
Notação Infixa NPR
A + B * C A B C * +
A * ( B + C ) A B C + *
( A + B ) / ( C - D ) A B + C D - /
( A + B ) / ( C - D ) * E A B + C D - / E *
Para converter uma expressão infixa para NPR, usamos o algoritmo abaixo:
1. Parentetizar completamente a expressão (definir a ordem de avaliação).
2. Varrer a expressão da esquerda para a direita e, para cada símbolo:
3. Se for parêntese de abertura, ignorar;
4. Se for operando, copiar direto para a saída;
5. Se for operador, empilhá-lo;
6. Se for parêntese de fechamento, copiar para a saída o último operador empilhado. 
Para exemplificar a aplicação do algoritmo, veja, na Tabela 4, a seguir, como usamos uma 
pilha para converter a expressão A + B * C - D em NPR.Note que o primeiro passo do 
algoritmo é parentetizar a expressão, ou seja, colocar parênteses para definir as prioridades. 
Portanto a expressão a ser analisada a partir do passo 2 é ( ( A + ( B * C ) ) - D ) 
 
Tabela 4 – Aplicação do Algoritmo para conversão em NPR
Simbolo Pilha Saída
( P: [ ]
( P: [ ]
A P: [ ] A
+ P: [ + ] A
( P: [ + ] A
15
B P: [ + ] A B
* P: [ +, * ] A B
C P: [ +, * ] A B C
) P: [ + ] A B C *
) P: [ ] A B C * +
- P: [ - ] A B C * +
D P: [ - ] A B C * + D
) P: [ ] A B C * + D -
O problema do algoritmo apresentado é que precisamos parentetizar a expressão antes 
de colocar no programa, porém, na prática, o usuário não utiliza dessa forma a expressão. A 
prioridade deve ser dada pelo usuário, para que o programa saiba por onde começar. Para 
resolver esse problema, usaremos uma função chamada “prio”, que dá a prioridade dos 
operadores da expressão. A função é apresentada na Figura 6 a seguir: 
Figura 6 – Função prio para definir prioridades das operações
 
Portanto, o novo algoritmo pode ser descrito nos seguintes passos: 
1. Inicie com uma pilha vazia.
2. Realize uma varredura na expressão infixa, copiando todos os identificadores encontrados 
diretamente para a saída.
a. Ao encontrar um operador:
I. enquanto a pilha não estiver vazia e houver, no seu topo, um operador com prioridade 
maior ou igual ao encontrado, desempilhe o operador e coloque-o na saída.
II. empilhe o operador encontrado.
b. Ao encontrar um parêntese de abertura, empilhe-o.
c. Ao encontrar um parêntese de fechamento, remova um símbolo da pilha e copie-o na saída 
até que seja desempilhado o parêntese de abertura correspondente.
3. Ao final da varredura, esvazie a pilha e copie para a saída os símbolos removidos.
16
Unidade: Listas Lineares
Percorrendo qualquer expressão em notação polonesa reversa, da esquerda para a direita, 
ao encontrarmos um operador, sabemos que deve operar os dois últimos valores pelos quais 
passamos. Percebemos, novamente, a ideia de que “os últimos serão os primeiros processados” 
e, novamente, a aplicação de pilhas.
Vejamos um exemplo na expressão em NPR:
  AB+CD-/E*
Vamos atribuir valores numéricos às variáveis da expressão a ser avaliada:
  A= 7; B= 3; C= 6; D= 4; E= 9.
Agora, seguiremos o algoritmo a seguir:
1. Iniciamos com uma pilha vazia.
2. Varremos a expressão da esquerda para a direita e para cada elemento encontrado:
3. Se for operando, empilhar;
4. Se for operador, desempilhar os dois últimos valores, efetuar a operação com eles e empilhar de 
volta o resultado obtido.
No final do processo, o resultado da avaliação estará no topo da pilha. Veja a 
aplicação do Algoritmo na Tabela 5 a seguir:
Tabela 5 – Aplicação do Algoritmo para Avaliar a Expressão
Elemento Ação Pilha
A Empilha valor de A P:[7]
B Empilha valor de B P:[7,3]
+
Desempilha um y=3
Desempilha outro x=7
Empilha resposta x+y=10
P:[10]
C Empilha valor de C P:[10,6]
D Empilha valor de D P:[10,6,4]
-
Desempilha um y=4
Desempilha outro x=6
Empilha resposta x-y=2
P:[10,2]
/
Desempilha um y=2
Desempilha outro x=10
Empilha resposta x/y=5
P:[5]
E Empilha valor de E P:[5,9]
*
Desempilha um y=9
Desempilha outro x=5
Empilha resposta x*y=45
P:[45]
17
Uma alternativa para dar valores aos operandos é criar um vetor de 26 posições (máximo de 
letras que pode ser utilizado) e, para cada letra encontrada na expressão, perguntar o seu valor.
Filas
Uma fila é um tipo especial de lista linear em que as inserções são realizadas num extremo 
enquanto as remoções são feitas no outro. O extremo em que os elementos são inseridos é 
denominado final da fila e aquele em que são removidos é denominado começo da fila. 
A ordem de saída dos elementos corresponde diretamente à ordem de entrada dos mesmos 
na fila, de modo que os primeiros elementos que entram serão os primeiros a sair, caracterizando 
uma estrutura FIFO (First-In/First-Out). 
A palavra queue, da língua inglesa, significa fila. As duas operações básicas que 
uma fila suporta são:
 » enqueue: insere um elemento no final da fila.
 » dequeue: remove um elemento do começo da fila.
Veja, na Tabela 6, a seguir, um exemplo de manipulação de Filas, sendo F uma fila.
Tabela 6 – Manipulação de Filas
Operação Estudo da Fila
-------------- F:[ ]
F.enqueue(a) F:[ a ]
F.enqueue(b) F:[ a, b ]
F.enqueue(c) F:[ a, b, c ]
F.enqueue(d) F:[ a, b, c, d ]
F.dequeue() F:[ b, c, d ]
F.enqueue(F.dequeue()) F:[ c, d, b ]
F.enqueue(e) F:[ c, d, b, e ]
F.enqueue(F.dequeue()) F:[ d, b, e, c ]
18
Unidade: Listas Lineares
Assim como nas pilhas a manipulação é feita apenas nas extremidades da estrutura, não 
havendo inserção e remoção no meio dela, podemos implementar as filas de maneira estática-
sequencial ou dinâmica-encadeada.
Ao tentarmos implementar uma Fila Estática Sequencial, deparamos com dois problemas:
  as inserções são feitas sempre no fim da fila e as remoções no começo. Como é inviável 
o deslocamento de todos os elementos para a esquerda na remoção, existirão espaços em 
branco no início da fila que não podem ser reaproveitados, pois a inserção é sempre à direita;
  quando inserimos elementos até ela ficar cheia (fim=MAX), temos que não há mais 
possibilidades de inserção. Porém, mesmo que removamos todos os elementos, ainda 
assim ela vai achar que está cheia por não ter possibilidade de inserção e vazia por não 
ter mais elementos. 
Para resolver o primeiro problema, podemos criar uma Fila Circular, em que, após o último 
elemento voltar para a primeira posição e se estiver disponível para inserção, podemos colocar 
mais elementos.
O segundo problema, para descobrir se a Fila está cheia ou vazia, basta criar um contador de 
elementos e, se estiver zerado, é porque a fila está vazia e, se estiver com MAX, ela está cheia.
Sendo assim, a melhor solução também é implementar apenas com alocação Dinâmica-
Encadeada. Para criarmos uma Fila Dinâmica, ainda vamos usar a notação na qual cada 
elemento da Fila será um nó contendo um dado e o endereço do próximo nó da Fila através de 
Listas Simplesmente Encadeadas. Porém, a Fila, por ser uma estrutura FIFO, deve manipular as 
duas extremidades (começo e final), pois as inserções são feitas no final da Fila e as remoções 
no começo. Além disso, usaremos um contador para armazenar a quantidade de elementos que 
a Fila possui. 
Veja, na Figura 7, a seguir, a implementação de uma Fila Dinâmica Encadeada usando a 
mesma classe Node que foi utilizada na nossa Pilha Dinâmica.
19
Figura 7 – Fila Dinâmica Encadeada
 
Aplicação de Filas
Apesar da simplicidade do conceito de Filas, ele tem se mostrado essencial para o 
desenvolvimento de muitas aplicações, sobretudo quando se trata de simular, no computador, 
situações reais, nas quais algum tipo de atendimento é modelado.
Em resumo, qualquer aplicação em que a ordem de entrada é a mesma ordem de saída dos 
elementos a mesma é candidata .
20
Unidade: Listas Lineares
Uma interessante aplicação de Fila em computação gráfica são os algoritmos para colorir 
regiões de desenhos, representados sob a forma de matrizes de pontos. Uma região de um 
desenho é um conjunto de pontos conectados entre si e que têm a mesma cor. Dizemos que dois 
pontos Pi e Pj estão conectados entre si se, e somente se, pudermos partir de Pi, incrementar (ou 
decrementar) sua abscissa (ou ordenada e não ambas) e chegar ao ponto Pj. 
Para colorir uma região R, podemos utilizar o seguinte algoritmo básico:
1. Obter um ponto inicial P0 de cor C0, seguramente pertencente à região R;
2. Obter uma nova cor C1 para a região R;
3. Colocar P0 numa Fila V, inicialmente vazia;
4. Enquanto a Fila V não esvaziar:
5. remover um ponto da fila V;
6. inserir em V todos os pontos conectados a P, cuja cor seja C0;
7. alterar a cor de P para C1.
Para poder simular o efeito, vamos representar uma imagem qualquer através de uma 
matriz, em que cada elemento da matriz representa um ponto do desenho(pixel) e cada pixel 
é discriminado pelascoordenadas da sua posição na matriz, conforme podemos ver na Figura 
8. Cada elemento da matriz armazena um valor correspondente à cor do ponto representado 
(0=branco, 1=cinza, 2=preto...)
Figura 8 – Representação de uma imagem através de uma matriz 
 
Na Figura 9, você pode conferir a implementação do Algoritmo para colorir regiões gráficas 
utilizando Filas.
21
Figura 9 – Programa para colorir regiões gráficas
 
22
Unidade: Listas Lineares
Material Complementar
1) Simuladores:
• http://www.cafw.ufsm.br/~bruno/disciplinas/estrutura_dados/applets/stack.html
• http://www.cafw.ufsm.br/~bruno/disciplinas/estrutura_dados/applets/queue.html
2) YouTube
• http://www.youtube.com/watch?v=z0WseEPdzd0
3) Wikipedia:
• http://pt.wikipedia.org/wiki/Estrutura_de_dados
PREISS, B. R. Estruturas de Dados e Algoritmos: Padroes de Projetos Orientados a 
Objetivos Com. Rio de Janeiro:Campus, 2001.
WIRTH, N. Algoritmos e Estruturas de Dados. Rio de Janeiro: Ltc-Livros Tecnicos e 
Cientificos, 1999.
MORAES, C. R. Estruturas de Dados e Algoritmos: Uma Abordagem Didatica. Sao Paulo: 
Berkeley, 2001.
FORBELLONE, A. L. V.; EBERSPACHER, H. F. Logica de Programacao: A Construcao de 
Algoritmos e Estrutura de Dados. 3. ed. Sao Paulo: Makron Books do Brasil, 2005.
VELOSO, P.; FURTADO, A.; SANTOS, C. S. Estruturas de Dados. 26. ed. Rio de Janeiro: 
Campus, 2003.
TENENBAUM, A. M.; LANGSAM, Y. Estruturas de Dados Usando C. Sao Paulo: Makron 
Books do Brasil, 2005.
DROZDEK, A. Estrutura de Dados e Algoritmos em C++. São Paulo: Pioneira Thomson 
Learning, 2005.
ASCENCIO, Ana F.G., Estruturas de Dados: algoritmos, análise da complexidade e 
implementações em Java e C/C++ São Paulo: Pearson Prendice Hall, 2010
DEITEL, Harvey M., Java Como Programar, 8.a edição, São Paulo: Pearson Prentice Hall, 2010
23
Referências
ASCENCIO, Ana F.G. Aplicações das Estruturas de Dados em Delphi. São Paulo: Pearson 
Prendice Hall, 2005.
GOODRICH, M. T.; TAMASSIA, R. Estruturas de Dados e Algoritmos em Java. 2. ed. 
Porto Alegre: Bookman, 2002.
LAFORE, R. Estruturas de Dados & Algoritmos em Java. Rio de Janeiro: Ciência 
Moderna, 2004.
PEREIRA, S. L. Estruturas de Dados Fundamentais: Conceitos e Aplicações. 9. ed. São 
Paulo: Erica, 2001.
WEISS, M. A. Data Structures And Algorithm Analysis In Java. 2. ed. Massachusetts: 
Addison-Wesley, 2007.
24
Unidade: Listas Lineares
Anotações
www.cruzeirodosulvirtual.com.br
Campus Liberdade
Rua Galvão Bueno, 868
CEP 01506-000
São Paulo SP Brasil 
Tel: (55 11) 3385-3000
www.cruzeirodosulvirtual.com.br
Rua Galvão Bueno, 868
Tel: (55 11) 3385-3000

Continue navegando