Você está na página 1de 116

Listas, Filas e

Pilhas
E N T E N D A O F U N C I O N A M E N TO D A S P R I N C IPA IS
E S T R U T U R A S D E D A D O S L I N E A R E S U T IL I Z A D A S N O S
S IS T E MA S C O M P U TA C I O N A I S E A S S U A S A P L I C A Ç Õ E S
Agenda

Introdução
Listas
Filas
Pilhas
Encadeamento
Conclusão
AVISO!
A partir desse módulo, parte-se da premissa
que o aluno possui TOTAL domínio em TAD,
Matrizes, Referências e Recursividade.
Caso esteja com dúvidas acerca de um
desses tópicos, revise-os IMEDIATAMENTE!
O professor está a sua disposição para ajudá-
lo no que for preciso.
Referências para Estudo

Java: Como Programar Estrutura de Dados e Algoritmos em Java


Paul Deitel Michael T. Goodrich

Capítulo 22 Capítulos 03, 05 e 06


FUNDAÇÃO EDSON QUEIROZ
UNIVERSIDADE DE FORTALEZA
ENSINANDO E APRENDENDO

introdução
PORQUE...
Relembrar é VIVER!
Estruturas de Dados
Estruturas de Dados são formas genéricas de organizar os dados
envolvidos na solução de um problema, possibilitando a consulta e
manipulação eficiente de dados.
◦ Exemplos: Banco de Dados, Ranking, Árvores Genealógicas.

Contudo, essas estruturas só possuem significado quando associadas a


um conjunto de operações que as manipulam.
◦ Essas operações visam, de um modo geral, administrar os dados contidos
nessas estruturas;
◦ São algoritmos que implementam ações específicas, de acordo com a
problemática abordada.
Mas quantas estruturas de
dados existem?
Estruturas de Dados (cont.)
Devido ao vasto domínio de problemas
existentes, diversas estruturas de dados
foram propostas afim de representar as mais
variadas operações envolvendo manipulações
de dados.
◦ Em outras palavras, existem estruturas de
dados específicas para cada necessidade de
uma aplicação;
◦ Portanto, a escolha da estrutura de dados
correta permite transformar problemas de alta
complexidade em um soluções triviais.
Estruturas de Dados (cont.)
Dentro do universos das estruturas
de dados, existem as estruturas de
dados clássicas:
◦ Estruturas Lineares
◦ Listas, Filas e Pilhas
◦ Estruturas Hierárquicas
◦ Árvores e suas variações
◦ Estruturas Relacionais
◦ Grafos
◦ Estruturas Dispersas
◦ Tabelas Hash

Essas estruturas são consideradas


clássicas devido ao vasto uso nas
aplicações.
FUNDAÇÃO EDSON QUEIROZ
UNIVERSIDADE DE FORTALEZA
ENSINANDO E APRENDENDO

Listas
APRENDA A TRABALHAR COM ESTRUTURAS
DE DADOS LINEARES
Como você realiza as compras
em um supermercado?
Compras no Supermercado
VAI COMPRAR É AS
COISAS PRA CASA,
SEU CABA
SENREGONHA!É hoje que eu compro
as minhas cevas!

Tomate Queijo
Carne Leite
Melão Margarina
Cheiro Verde Presunto
Mamão Cajuína
Pão Fralda
O que pode-se extrair desse
exemplo?
Reflexão A partir do cenário proposto, é possível
extrair algumas características importantes:
◦ Todos os elementos possuem a mesma
importância;
◦ A adição de itens na lista não segue ordem
alguma;
◦ De forma semelhante, a remoção também
não segue regra alguma.

Em outras palavras, os elementos podem


ser adicionados/removidos em qualquer
ponto dessa estrutura.
Listas
Listas são estruturas de dados caracterizadas por armazenar seus
elementos de forma linear e permitir a manipulação direta de qualquer
um desses elementos.
◦ Os elementos armazenados nessa estrutura podem ser primitivos ou TADs;
◦ Considerada uma estrutura bastante flexível, uma vez que a ordem dos
elementos é irrelevante para o armazenamento e manipulação dos dados.

Em cenários complexos, é possível utilizar uma lista para armazenar


outras listas como elementos.
◦ Exemplo: Uma lista de turmas, onde cada elemento, por sua vez, contém
uma lista de alunos.
Quais as principais características
das estruturas do tipo lista?
Definição Formal
Uma lista L é um conjunto de zero ou mais elementos, onde cada um
deles representa um valor.
◦ Importante lembrar que esse valor pode ser atômico (indivisível) ou outra
lista.

Uma lista L possui as seguintes características:


◦ x1 é o primeiro elemento (mais à esquerda) da lista, também chamado de
início ou header da lista;
◦ xn é o último elemento (mais à direita) da lista, também chamado de final ou
trailer da lista, sendo n a quantidade de elementos contidos nessa lista;
◦ x2, x3, ..., xn-1 são os elementos que compõem o restante da lista;
◦ Sendo xi o elemento da lista localizado na posição i, o seu antecessor é xi-1 e
seu sucessor é xi+1.
Características de uma Lista
(cont.)

x1 xn
(header) x2 x3 x...i-1 ...
xi x...i+1 ... (trailer)

5 4 7 9 1 8 6 2
Quais são as operações básicas que
caracterizam uma lista?
Operações Básicas
Uma listas básica possuem as seguintes operações:
◦ Criar uma nova lista – Lista()
◦ Retornar a quantidade de elementos – getQuantidade()
◦ Verificar se a lista está vazia – estaVazia()
◦ Verificar se a lista está cheia, caso possua um limite – estaCheia()
◦ Acessar o elemento na posição i – retornarElemento(i)
◦ Inserir um elemento e na posição i – adicionarPosicao(e, i)
◦ Inserir um elemento e no início – adicionarInicio(e)
◦ Inserir um elemento e no final – adicionarFinal(e)
◦ Remover o elemento e na posição i – removerPosicao(i)
◦ Remover o elemento no inicio – removerInicio()
◦ Remover o elemento no final – removerFinal()
Operações Básicas (cont.)
Dados da Lista:
• 7
Quantidade: 8
• Primeiro Elemento: 5

X
• 2
Último Elemento: 9

5 4 7 9
1 1
8 8
6 6
2 29

8 9
OPERAÇÃO: Remover elemento
Inserir o elemento 89 (oito)
o elemento (nove) no6ªfinal
da 4ªna
posição
posição
Operações Básicas (cont.)
A partir dessas operações básicas,
é possível definir outras operações
mais específicas, como:
◦ Verificar se um elemento está na
lista e qual a sua posição;
◦ Comparar, dividir, combinar ou
inverter listas;
◦ Realizar operações em volume com
os elementos;
◦ Ordenar os elementos da lista.
Mas como implementar uma lista e
suas operações?
Implementação
Os elementos de uma lista estão dispostos na forma linear.
◦ Ou seja, os elementos desse conjunto são consecutivos, vizinhos;

Através dessa perspectiva, é intuitivo organizar os elementos na memória do


sistema computacional de forma semelhante a como esses elementos estão
dispostos no mundo real.
◦ Alinhando, portanto, as ordens lógica (real) e física (memória) dos elementos desse
conjunto;
◦ Essa forma de armazenamento é chamada de Alocação Sequencial.

Para organizar os elementos em um local contiguo na memória, utiliza-se


variáveis/atributos vetoriais.
◦ Essa contiguidade física aumenta o desempenho de operações básicas envolvendo
grupos de elementos sequenciais da lista;
◦ Por trabalhar com vetores, no entanto, essas listas possuem um tamanho máximo
definido.
Implementação (cont.)
Na programação orientada a objetos, uma lista sequencial é descrita através
de uma classe com as seguintes propriedades:
◦ Essa classe contém um atributo que representa o vetor de elementos. O tipo de
dado desse atributo é igual ao do elemento nele armazenado, e sua inicialização
ocorre no construtor;
◦ Outro atributo dessa classe representa o número de elementos inseridos na lista.
Esse atributo possibilita controlar o número real de elementos inseridos, uma vez
que o tamanho do vetor representa o máximo de elementos;
◦ Devido a indexação de vetores iniciar em 0 (zero), o número de elementos
também representa a próxima posição (índice do vetor) a ser informada na
inserção de um novo elemento ao final da lista;
◦ Toda operação de inserção deve verificar se a adição do novo elemento rompe o
tamanho do vetor;
◦ Toda operação de remoção deve verificar se a operação está retirado um
elemento de uma lista já vazia.
RamúBrinka?
IMPORTANTE!
Os algoritmos envolvidos na construção de
uma lista estão representados na forma de
pseudocódigos.
◦ Essa forma simplifica a compreensão da
lógica central dos algoritmos por parte do
aluno.
◦ Recursos específicos das linguagens de
programação, como visibilidade, tratamento
de exceções e interfaces não estão
presentes.

É dever do aluno adaptar os algoritmos


apresentados nas linguagens de
programação abordadas na disciplina.
Para representar uma lista, dois

Implementação (cont.) atributos são necessários: um


VETOR para armazenar os
elementos da lista e um inteiro
classe Lista que armazenará o NÚMERO DE
ELEMENTOS inseridos.
vetor_elementos[]
O construtor recebe o
numero_elementos NÚMERO MÁXIMO DE
ELEMENTOS (tamanho do
CONSTRUTOR(t) { vetor) e inicializa os atributos
da lista.
vetor_elementos  vetor inicializado com tamanho igual a
t
numero_elementos 0
}
Esse método verifica se a lista
encontra-se sem elementos,
LISTA_VAZIA() { retornando VERDADEIRO caso
se numero_elementos = 0 não existam elementos
inseridos.
retorne VERDADEIRO
senão
retorne FALSO
fim-se
}
Implementação (cont.) Esse método verifica se o vetor
está totalmente preenchido
com os elementos da lista,
LISTA_CHEIA() {
retornando VERDADEIRO caso
se numero_elementos >= vetor_elementos.tamanho - 1
afirmativo.
retorne VERDADEIRO
senão
retorne FALSO
A adição ao final da lista
fim-se consiste na inserção do novo
} elemento no PRIMEIRO ÍNDICE
NÃO UTILIZADO do vetor.
Antes da inserção, verifica-se se
ADICIONAR_FINAL(e) { a lista encontra-se cheia,
se LISTA_CHEIA() disparando um erro caso
erro “Estrutura cheia!” verdadeiro.
senão
indice  numero_elementos
vetor_elementos[indice]  e
numero_elementos  numero_elementos + 1
fim-se
}
Vai, Rocky!
A remoção ao final da lista
consiste na retirada e retorno

Implementação (cont.) do elemento localizado no


ÚLTIMO ÍNDICE UTILIZADO do
vetor pela lista. Antes da
inserção, verifica-se se a lista
REMOVER_FINAL() { encontra-se vazia, disparando
se LISTA_VAZIA() um erro caso verdadeiro.
erro “Estrutura vazia!”
senão
indice  numero_elementos – 1
elemento_removido  vetor_elementos[indice] A adição ao início da lista
numero_elementos  indice consiste na inserção do novo
retorne elemento_removido
elemento no PRIMEIRO ÍNDICE
fim-se
do vetor. Antes da inserção,
}
verifica-se se a lista encontra-se
cheia, gerando um erro caso
ADICIONAR_INICIO(e) {
se LISTA_CHEIA()
isso ocorra.
erro “Estrutura cheia!”
senão
para indice de numero_elementos a 0 faça
vetor_elementos[indice] 
vetor_elementos[indice - 1]
fim-para
vetor_elementos[0]  e
numero_elementos  numero_elementos + 1
fim-se
A remoção ao inicio da lista
consiste na retirada e retorno
do elemento localizado no

Implementação (cont.) PRIMEIRO ÍNDICE do vetor pela


lista. A lista deve possuir no
mínimo um elemento, ou um
REMOVER_INICIO() { erro será disparado.
se LISTA_VAZIA()
erro “Estrutura vazia!”
senão
elemento_removido  vetor_elementos[0]
para indice de 1 a numero_elementos - 1 faça
vetor_elementos[indice - 1] 
vetor_elementos[indice]
fim-para
numero_elementos  numero_elementos – 1
retorne elemento_removido
fim-se
}
Melhorar o código você deve,
Padawan!
A adição em uma posição
específica aproveita-se dos
métodos já implementados.

Implementação (cont.) Caso a posição informada seja


no início ou no final da lista, os
respectivos métodos são
ADICIONAR_POSICAO(e, i) { invocados.
se LISTA_CHEIA()
erro “Estrutura cheia!” Nos demais casos, os
senão elementos POSTERIORES à
se i <= 0 posição desejada são
EMPURRADOS para a direita.
ADICIONAR_INICIO(e)
Somente então o novo
senão se i >= numero_elementos elemento é inserido.
ADICIONAR_FINAL(e)
senão
para indice de numero_elementos a i faça
vetor_elementos[indice] 
vetor_elementos[indice - 1]
fim-para
vetor_elementos[i]  e
numero_elementos  numero_elementos + 1
fim-se
A remoção em uma posição
específica aproveita-se dos
métodos já implementados.

Implementação (cont.)
REMOVER_POSICAO(i) {
Caso a posição informada seja
no início ou no final da lista, os
se LISTA_VAZIA() respectivos métodos são
erro “Estrutura vazia!” invocados.
senão
se i <= 0
Nos demais casos, o elemento
retorne REMOVER_INICIO()
inserido é copiado e os
senão se i >= numero_elementos
elementos POSTERIORES à
retorne REMOVER_FINAL()
posição desejada são
senão
EMPURRADOS para a
elemento_removido  vetor_elementos[i]
esquerda.
para indice de i a numero_elementos - 1 faça
vetor_elementos[indice] 
vetor_elementos[indice + 1]
fim-para
numero_elementos  numero_elementos – 1
retorne elemento_removido
fim-se
fim-se
}

fim-classe Lista
Eureka! Conseguimos!
O que acontece com os valores do vetor
na memória após a remoção de um
elemento?
DEVER DE CASA
Implementar a estrutura de dados lista
utilizando as linguagens de programação
Java e C++.
◦ Recomenda-se a escrita inicial da lógica central
da estrutura apresentada.
◦ Após a compreensão do código escrito,
adicionar os recursos avançados da linguagem.
◦ Lembre-se de validar a sua estrutura através
da montagem de exemplos que manipulam
essas estruturas.
Recursos Avançados
É possível aplicar diversos fundamentos e recursos da programação
orientada a objetos no desenvolvimento das listas.
◦ Interfaces possibilitam a definição dos métodos obrigatórios da lista,
qualquer que seja o seu tipo (sequencial ou encadeada);
◦ O encapsulamento garante a correta visibilidade das propriedades (atributos
e métodos) da lista;
◦ O tratamento de exceções permite personalizar a experiência do usuário com
o programa e o tratamento de eventos especiais das listas (lista cheia ou
vazia, posições inválidas, etc.);
◦ O uso de tipos genéricos possibilita a geração de estruturas de dados
voltadas para o armazenamento e manipulação de diferentes tipos de dados
(primitivos ou abstratos).
FUNDAÇÃO EDSON QUEIROZ
UNIVERSIDADE DE FORTALEZA
ENSINANDO E APRENDENDO

IMPLEMENTAÇÃO DE LISTAS

DEMONSTRAÇÃO
Casos Especiais
Em muitos cenários, é útil impor determinadas restrições quanto a
manipulação de certas estruturas de dados.
◦ Essas limitações afetam a visibilidade e as operações disponibilizadas pela
estrutura;
◦ Ajuda na modelagem computacional de certos processos que ocorrem no
mundo real.

Ao longo dos anos, foram identificadas algumas disciplinas


especializadas de acesso e manipulação de listas.
◦ As duas especializações mais importantes das listas são as Filas e as Pilhas.
FUNDAÇÃO EDSON QUEIROZ
UNIVERSIDADE DE FORTALEZA
ENSINANDO E APRENDENDO

Filas
ENTENDA OS FUNDAMENTOS ENVOLVIDOS
NA REPRESENTAÇÃO DESSA
ESPECIALIZAÇÃO DE LISTAS
Como funciona um
atendimento bancário?
Atendimento Bancário

SENHA

20
O que pode-se extrair desse
exemplo?
Reflexão A partir do cenário proposto, é possível
extrair algumas características importantes:
◦ Todos os elementos possuem a mesma
importância;
◦ A adição de itens sempre ocorre no final do
conjunto;
◦ A remoção de elementos sempre ocorre no
início do conjunto.

Em outras palavras, os elementos sempre


são adicionados no final da estrutura e
removidos no início dessa mesma
estrutura.
Filas
Filas são estruturas de dados caracterizadas por armazenar seus
elementos de forma linear e permitir a manipulação direta apenas dos
elementos contidos em suas extremidades.
◦ Trata-se de um caso especial de listas;
◦ As operações de inserção sempre ocorrem na extremidade final desse tipo
de lista;
◦ As operações de consulta e remoção sempre ocorrem na extremidade inicial
desse tipo de lista;
Filas (cont.)
As restrições na manipulação dos
elementos em uma fila resultam
em um conceito conhecido como
FIFO (First In, First Out).
◦ O primeiro elemento que é inserido
na fila é o primeiro a ser removido;
◦ Em outras palavras, os elementos
mais antigos são os primeiros a
serem retirados da estrutura.
Quais as principais características
das estruturas do tipo fila?
Definição Formal
Uma fila F é um conjunto de zero ou mais elementos, onde cada um
deles representa um valor, e que possui as seguintes propriedades:
◦ x1 é o primeiro e mais antigo elemento (mais à esquerda) da fila, também
chamado header da fila;
◦ xn é o último e mais recente elemento (mais à direita) da fila, também
chamado de trailer da fila, sendo n a quantidade de elementos contidos
nessa lista;
◦ x2, x3, ..., xn-1 são os elementos que compõem o restante da fila;
◦ Sendo xi o elemento da fila localizado na posição i, o seu antecessor é xi-1 e
seu sucessor é xi+1.
Quais são as operações básicas que
caracterizam uma fila?
Operações Básicas
Uma fila básica possuem as seguintes operações:
◦ Criar uma nova fila – Fila()
◦ Retornar a quantidade de elementos – getQuantidade()
◦ Verificar se a fila está vazia – estaVazia()
◦ Verificar se a fila está cheia, caso possua um limite – estaCheia()
◦ Acessar o elemento no início da fila – retornarElemento()
◦ Inserir um elemento e no final da fila – enfileirar(e)
◦ Remover o elemento e no início da fila – desenfileirar()

Por ser um caso especial de listas, os métodos enfileirar e desenfileirar


possuem as mesmas implementações das operações de adicionar ao
final e remover no início das listas.
◦ Em inglês, são chamadas de enqueue e dequeue.
Filas (cont.)
Dados da Fila:
• Quantidade: 4
20
1
3
• Primeiro Elemento: 58
• Último Elemento: 3
8
5
7

X
5
8 8
7 7
3 3

OPERAÇÃO:
OPERAÇÃO: Inserir
Remover o elemento
elemento3
7
5
8
Mas como implementar uma fila e
suas operações?
Implementação
Assim como as listas, as filas
suportam a alocação sequencial
de seus elementos na forma de
um vetor.
◦ Garantindo o alinhamento das
ordens lógica e física dos
elementos desse conjunto.

Portanto, todos os detalhes


inerentes a implementação de
uma lista estão também
envolvidos na definição de uma
fila.
DEVER DE CASA
Implementar as estruturas de
dados do tipo fila apresentadas
utilizando as linguagens de
programação Java e C++.
◦ Recomenda-se a escrita inicial da
lógica central da estrutura
apresentada.
◦ Após a compreensão do código
escrito, adicionar os recursos
avançados da linguagem.
◦ Lembre-se de validar a sua
estrutura através da montagem de
exemplos que manipulam essas
estruturas.
FUNDAÇÃO EDSON QUEIROZ
UNIVERSIDADE DE FORTALEZA
ENSINANDO E APRENDENDO

IMPLEMENTAÇÃO DE FILAS

DEMONSTRAÇÃO
FUNDAÇÃO EDSON QUEIROZ
UNIVERSIDADE DE FORTALEZA
ENSINANDO E APRENDENDO

Pilhas
ENTENDA OS FUNDAMENTOS ENVOLVIDOS
NA REPRESENTAÇÃO DESSA
ESPECIALIZAÇÃO DE LISTAS
Como funciona um setor de
estoque e armazenamento de
produtos?
Setor de Estoque
TU É DOIDO, MAH!
NUM ISQUECE DE
IMPLILHAR DAS
AH, MAH! Esqueci
CAIXAS! Acabou o expediente!
que a caixa vermelha
Hora de ir pra casa!
é noutro lugar!
O que pode-se extrair desse
exemplo?
Reflexão A partir do cenário proposto, é possível
extrair algumas características importantes:
◦ Todos os elementos possuem a mesma
importância;
◦ A adição e remoção de itens sempre ocorre
no topo do conjunto.

Em outras palavras, os elementos sempre


são adicionados e removidos no final dessa
estrutura.
Pilhas
Pilhas são estruturas de dados caracterizadas por armazenar seus
elementos de forma linear e permitir a manipulação direta apenas dos
elementos contidos em suas extremidades.
◦ Trata-se de um caso especial de listas;
◦ As operações de consulta, inserção e remoção sempre ocorrem na
extremidade final desse tipo de lista.

As restrições na manipulação dos elementos em uma pilha resultam em


um conceito conhecido como LIFO (Last In, First Out).
◦ O último elemento que é inserido na fila é o primeiro a ser removido;
◦ Em outras palavras, os elementos mais recentes são os primeiros a serem
retirados da estrutura.
Quais as principais características
das estruturas do tipo pilha?
Definição Formal
Uma pilha P é um conjunto de zero ou mais elementos, onde cada um
deles representa um valor, e que possui as seguintes propriedades:
◦ x1 é o primeiro e mais antigo elemento (mais à esquerda) da pilha, também
chamado header da pilha;
◦ xn é o último e mais recente elemento (mais à direita) da fila, também
chamado de trailer da pilha, sendo n a quantidade de elementos contidos
nessa pilha;
◦ x2, x3, ..., xn-1 são os elementos que compõem o restante da pilha;
◦ Sendo xi o elemento da pilha localizado na posição i, o seu antecessor é xi-1 e
seu sucessor é xi+1.
Quais são as operações básicas que
caracterizam uma pilha?
Operações Básicas
Uma pilha básica possuem as seguintes operações:
◦ Criar uma nova pilha – Pilha()
◦ Retornar a quantidade de elementos – getQuantidade()
◦ Verificar se a pilha está vazia – estaVazia()
◦ Verificar se a pilha está cheia, caso exista um limite – estaCheia()
◦ Acessar o elemento no topo da pilha – retornarElemento()
◦ Inserir um elemento e no topo da pilha – empilhar(e)
◦ Remover o elemento e no topo da pilha – desempilhar()

Por ser um caso especial de listas, os métodos empilhar e desempilhar


possuem as mesmas implementações das operações de adicionar ao
final e remover no final das listas.
◦ Em inglês, essas operações são chamadas de push e pop.
Pilhas (cont.)
PUSH POP

3
7
5

PILHA
Mas como implementar uma pilha e
suas operações?
Implementação
Assim como as listas, as pilhas
suportam a alocação sequencial
de seus elementos na forma de
um vetor.
◦ Garantindo o alinhamento das
ordens lógica e física dos
elementos desse conjunto.

Portanto, todos os detalhes


inerentes a implementação de
uma lista estão também
envolvidos na definição de uma
fila.
DEVER DE CASA
Implementar as estruturas de
dados do tipo pilha apresentadas
utilizando as linguagens de
programação Java e C++.
◦ Recomenda-se a escrita inicial da
lógica central da estrutura
apresentada.
◦ Após a compreensão do código
escrito, adicionar os recursos
avançados da linguagem.
◦ Lembre-se de validar a sua
estrutura através da montagem de
exemplos que manipulam essas
estruturas.
FUNDAÇÃO EDSON QUEIROZ
UNIVERSIDADE DE FORTALEZA
ENSINANDO E APRENDENDO

IMPLEMENTAÇÃO DE PILHAS

DEMONSTRAÇÃO
FUNDAÇÃO EDSON QUEIROZ
UNIVERSIDADE DE FORTALEZA
ENSINANDO E APRENDENDO

Encadeamento
APRENDA A TRABALHAR COM O RECURSO
DE ENCADEAMENTO PARA REPRESENTAR
ESTRUTURAS LINEARES
Quais as desvantagens em trabalhar
com a alocação sequencial?
Limitações da Alocação
Sequencial
A alocação sequencial possui as seguintes
desvantagens:
◦ O espaço ocupado pelo vetor para armazenar
os elementos da estrutura permanece
reservado na memória, mesmo quando a lista
não ocupa todo o espaço desse vetor;
◦ Além disso, não é possível armazenar mais
elementos do que a quantia máxima definida
ao criar o vetor, gerando o erro do estouro.
Qual seria a outra abordagem para
definir e manipular estruturas
lineares?
Alocação Encadeada
Para resolver esses problemas, foi desenvolvida a técnica da alocação
encadeada.
◦ Na alocação encadeada, cada elemento é inserido dentro de um TAD
chamado nó, o qual funciona como um container;
◦ Nessa técnica, os nós que compõem a estrutura estão espalhados na
memória, mas ligados através de referências contidas em cada um desses
elementos;
◦ Cada nó armazena, além do elemento, as referências que ligam esse objeto a
outros nós;
◦ Em outras palavras, a posição relativa dos nós na memória não corresponde
à sua posição lógica na estrutura.
Alocação Encadeada (cont.)
Estruturas de dados baseadas na alocação encadeada possuem os
seguintes benefícios:
◦ As diversas operações relativas a manipulação dos nós na estrutura, como
inserções e remoções, resumem-se a manipulação de referências;
◦ Ao manipular referências, não há movimentação dos elementos na memória,
aumentando o desempenho dessas operações;
◦ A alocação de memória para armazenamento os elementos na estrutura é
gradual, permitindo um crescimento praticamente indefinido da estrutura.
Como a alocação encadeada
possibilita a implementação de uma
lista?
Lista Encadeada
Em uma lista encadeada, os nós que armazenam os elementos
encontram-se aleatoriamente dispostos na memória, sendo interligados
através das referências.
◦ Cada referência indica a posição de um nó na memória.

Na programação orientada a objetos, um nó é descrito através de uma


classe com as seguintes propriedades:
◦ Essa classe contém um atributo que representa o elemento da lista. O tipo de
dado desse atributo é igual ao do elemento nele armazenado, e sua
inicialização ocorre no construtor, como parâmetro de entrada;
◦ Outro atributo dessa classe representa a referência para o próximo nó da
lista. Na criação do nó, esse atributo é inicializado com o valor nulo, ou seja,
não aponta para nenhum outro nó na memória.
Lista Encadeada (cont.)
Como uma lista é um conjunto de nós interligados, faz-se necessária a
implementação de uma segunda classe, na qual a estrutura de dados é
implementada.
◦ Portanto, essa classe é responsável por conter o ponto de partida da lista e controlar as
operações de manipulação dos nós contidos na estrutura;
◦ Essa classe contém um atributo que armazenará uma referência para o primeiro nó da lista
(inicio/header);
◦ É possível o armazenamento de outros atributos, como o número de elementos
armazenados, para fins de simplificação de algumas operações de manipulação.
◦ Embora esses novos atributos aumentam o número de escritas realizadas a cada operação realizada na lista.
Lista Encadeada (cont.)
Para determinar o elemento
que inicia a lista, uma Sempre o último elemento
referência para o primeiro (mais recente) da lista aponta
elemento é implementada. para NULO.
Essa referência especial é
chamada de HEADER/INÍCIO, e
INÍCIO é definida como atributo da
classe LISTA.

5 8 7 10 2 3

Conforme apresentado, cada O segundo atributo de um nó


NÓ possui dois atributos. O guarda a REFERÊNCIA (localização)
primeiro armazena o elemento para o próximo nó.
(dado) que compõe a lista.
Lista Encadeada (cont.)
Em suma, ao utilizar encadeamento, duas
classes são necessárias para implementar
uma lista.
◦ Uma que representa um nó, com seu elemento
e referência para o próximo nó/
◦ Uma que representa a estrutura de dados
(lista), com o atributo header e métodos para a
manipulação da estrutura.
Como seria a operação de inserção
de um elemento na lista encadeada?
Lista Encadeada (cont.)

INÍCIO

5 8
X 7 10 2 3

14
Como seria a operação de remoção
de um elemento na lista encadeada?
Lista Encadeada (cont.)

INÍCIO

5 8 7
X X
10 2 3
Mas e quem removerá o nó
“excluído” da memória?
Mas afinal, isso é
implementável? Ou seria
mágica? Ou seria mistério?
IMPORTANTE!
Os algoritmos envolvidos na construção de
uma lista estão representados na forma de
pseudocódigos.
◦ Essa forma simplifica a compreensão da
lógica central dos algoritmos por parte do
aluno.
◦ Recursos específicos das linguagens de
programação, como visibilidade, tratamento
de exceções e interfaces não estão
presentes.

É dever do aluno adaptar os algoritmos


apresentados nas linguagens de
programação abordadas na disciplina.
Implementação Para representar um nó, dois
atributos são necessários: um
que representa o ELEMENTO a
classe Nó ser armazenado e uma
REFERÊNCIA para o PRÓXIMO
nó.
elemento_armazenado
proximo_no

CONSTRUTOR(e) {
O construtor recebe o
elemento_armazenado  e ELEMENTO a ser armazenado e
proximo  NULO inicializa a referência como
} NULO.

fim-classe Nó
Para representar uma lista
encadeada, um único atributo é
necessário: uma REFERÊNCIA

Implementação (cont.) do tipo NÓ, a qual aponta para


o INÍCIO da lista.

classe Lista
O construtor apenas inicializa a
lista vazia, informando que o
inicio_lista início da lista aponta para
NULO.
CONSTRUTOR() {
inicio_lista  NULO
Esse método verifica se a lista
}
encontra-se sem elementos,
retornando VERDADEIRO caso
LISTA_VAZIA() { não existam elementos
inseridos.
se inicio_lista == NULO
retorne VERDADEIRO
senão
retorne FALSO
fim-se
}
A inserção de um elemento no
início de uma lista encadeada
consiste na criação de um
NOVO NÓ. Após a criação desse

Implementação (cont.)
ADICIONAR_INICIO(e) {
contêiner, ele apontará para o
INÍCIO ATUAL da lista caso a
mesma não esteja vazia. Em
novo  recebe um novo nó, inicializado com o valor e
se !LISTA_VAZIA() seguida, esse novo nó se torna
novo.proximo_no  inicio_lista o NOVO INÍCIO da estrutura,
fim-se independente da lista estar
inicio_lista  novo
vazia ou não.
}

ADICIONAR_FINAL(e) {
novo  recebe um novo nó, inicializado com o valor e
A inserção de um elemento no
se LISTA_VAZIA()
final de uma lista encadeada
consiste na criação de um
inicio_lista  novo
NOVO NÓ. Após a criação
senão
desse contêiner, um NÓ
auxiliar  inicio_lista
AUXILIAR é criado, para que o
enquanto auxiliar.proximo_no != NULO faça
código possa percorrer toda a
auxiliar  auxiliar.proximo_no
estrutura até chegar ao último
fim-enquanto
elemento (aquele cujo o
auxiliar.proximo_no  novo próximo nó é NULO). Em
seguida, o PRÓXIMO NÓ do
fim-se auxiliar será o novo nó criado.
}
A remoção ao início da lista
consiste na alteração da
REFERÊNCIA do início da lista

Implementação (cont.)
REMOVER_INICIO() {
removido  NULO
para o SEGUNDO ELEMENTO (o
próximo nó a partir do início da
lista, representado pelo nó
se LISTA_VAZIA() AUXILIAR) e o retorno do
erro “Estrutura vazia!”
primeiro elemento.
senão
removido  inicio_lista
inicio_lista  inicio_lista.proximo_no
fim-se
A remoção ao final da lista
retorne removido consiste na alteração da
} REFERÊNCIA do próximo nó do
REMOVER_FINAL() { PENÚLTIMO ELEMENTO
removido  NULO (representado pelo nó
se LISTA_VAZIA() AUXILIAR) para NULO e o
erro “Estrutura vazia!” retorno do último elemento da
senão
lista.
removido  auxiliar  inicio_lista
enquanto removido.proximo_no != NULO faça
auxiliar  removido
removido  removido.proximo_no
fim-enquanto
auxiliar.proximo_no  NULO
fim-se
retorne removido
}
A adição em uma posição
específica aproveita-se dos
métodos já implementados.

Implementação (cont.) Caso a posição informada seja


no início ou no final da lista, os
respectivos métodos são
ADICIONAR_POSICAO(e, i) { invocados.
se LISTA_VAZIA() ou i <= 1
ADICIONAR_INICIO(e)
senão
novo  recebe um novo nó, inicializado com o valor e
auxiliar  inicio_lista
indice  1
Nos demais casos, é realizada
enquanto indice < i e auxiliar != NULO faça
auxiliar  auxiliar.proximo_no uma varredura até o código
atingir o elemento ANTERIOR à
indice  indice + 1
posição desejada (representado
fim-enquanto pelo nó AUXILIAR). Em seguida,
se auxiliar == NULO o próximo do novo nó
ADICIONAR_FINAL(e) referenciará o próximo nó do
nó auxiliar, enquanto o próximo
senão
do nó auxiliar apontará para o
novo.proximo_no  auxiliar.proximo_no
novo nó. A ORDEM das
auxiliar.proximo_no  novo operações é ESSENCIAL.
A remoção em uma posição
específica aproveita-se dos
métodos já implementados.

Implementação (cont.)
REMOVER_POSICAO(i) {
Caso a posição informada seja
no início ou no final da lista, os
removido  NULO respectivos métodos são
se LISTA_VAZIA() ou i <= 1 invocados.
removido  REMOVER_INICIO()
senão
removido  auxiliar  inicio_lista
indice  1
enquanto indice < i e removido != NULO faça
auxiliar  removido
removido  removido.proximo_no
indice  indice + 1 Nos demais casos, é realizada
fim-enquanto uma varredura até o código
se removido == NULO atingir o elemento a ser
removido  REMOVER_FINAL() REMOVIDO e o elemento
senão ANTERIOR à posição desejada
auxiliar.proximo_no  removido.proximo_no (representado pelo nó
fim-se AUXILIAR). Em seguida, o
fim-se próximo do nó auxiliar
retorne removido referenciará o próximo nó do
} nó removido. A ORDEM das
fim-classe Lista operações é ESSENCIAL.
Filas e Pilhas Encadeadas

A implementação de filas e pilhas através do


uso de elementos encadeados segue os
mesmos padrões da lista.
◦ As mudanças limitam-se a disponibilizar nessas
estruturas especiais apenas as suas operações
exclusivas.
DEVER DE CASA
Implementar as estruturas de
dados encadeadas dos tipos lista,
fila e pilha utilizando as linguagens
de programação Java e C++.
◦ Recomenda-se a escrita inicial da
lógica central da estrutura
apresentada.
◦ Após a compreensão do código
escrito, adicionar os recursos
avançados da linguagem.
◦ Lembre-se de validar a sua
estrutura através da montagem de
exemplos que manipulam essas
estruturas.
FUNDAÇÃO EDSON QUEIROZ
UNIVERSIDADE DE FORTALEZA
ENSINANDO E APRENDENDO

IMPLEMENTAÇÃO DE LISTAS
ENCADEADAS

DEMONSTRAÇÃO
Qual a dificuldade de se trabalhar com listas
encadeadas com referência apenas para o próximo?
EncadeamentoAtéSimples
agora, os nós utilizados nas estruturas
de dados encadeamento armazenam
referenciam apenas o próximo elemento.
◦ Conhecido como Encadeamento Simples.

Essa abordagem limita operações que


necessitam varrer as estruturas em ambas
as direções.
Existem duas soluções para esse problema:
◦ Listas Circulares;
◦ Listas Duplamente Encadeadas.
Lista Circular
A partir de um nó em uma lista encadeada, não é possível atingir nó
algum que antecede o nó avaliado.
◦ Exemplo: A partir do nó localizado na posição 4, não é possível retornar ao nó
que encontra-se na posição 3.

Entretanto, se o atributo que referencia o próximo nó do último


elemento da lista apontar para o nó inicial da lista, torna-se possível
retornar ao início da lista e realizar novamente a varredura.
◦ Essa lista é conhecida como Lista Circular.
Lista Circular (cont.)

5 8 7 10 2 3
Mas para qual elemento o início da
lista irá apontar, uma vez que é
circular?
Lista Circular
Uma (cont.)
convenção adotada pelos desenvolvedores é
apontar o início da lista para o último elemento
inserido.
◦ Assim, obtém-se o acesso direto ao último e ao
primeiro elementos da lista.

As operações de inserção e remoção devem tratar


os casos especiais.
◦ Quando envolvem o primeiro ou o último elemento,
para manter a circularidade da lista.

O conceito de nó de cabeçalho pode também ser


aplicada à lista circular.
◦ Funciona como um cabeçote de leitura, o qual vai
navegando através dos elementos da lista.
◦ Nesse caso, não são os elementos da lista que
circulam, mas o cabeçalho que se desloca.
DEVER DE CASA
Implementar a estrutura de dado
encadeada do tipo lista circular
utilizando as linguagens de
programação Java e C++.
◦ Recomenda-se a escrita inicial da
lógica central da estrutura
apresentada.
◦ Após a compreensão do código
escrito, adicionar os recursos
avançados da linguagem.
◦ Lembre-se de validar a sua
estrutura através da montagem de
exemplos que manipulam essas
estruturas.
Lista Duplamente Encadeada
Embora uma lista circular apresente algumas vantagens sobre uma lista
encadeada simples, ambas as estruturas ainda possui limitações.
◦ Não é possível percorrer a estrutura no sentido contrário, forçando o uso de
estruturas de repetição para realizar operações no final da estrutura.

Com o objetivo de sanar estas limitações, foram desenvolvidas


estruturas onde cada elemento referencia seu antecessor e sucessor.
◦ Chamadas de Listas Duplamente Encadeadas;
◦ Consideradas as listas mais poderosas que existem.
Lista Duplamente Encadeada
(cont.)
Na programação orientada a objetos, uma lista duplamente encadeada difere da
mesma estrutura com encadeamento único nos seguintes aspectos:
◦ Na classe que representa o nó, é adicionado um terceiro atributo, responsável por
referenciar o nó anterior ao elemento. No caso do elemento em questão representar o
início da lista, o seu atributo anterior apontará para nulo;
◦ Na classe que representa a lista, um segundo atributo é adicionado para representar o
último elemento da lista (final/trailer);
◦ As operações de inserção e remoção passam a manipular, além dos elementos e das suas
referências para os próximos elementos, as referências relativas aos antecessores desses
elementos.
◦ A ordem das operações que manipulam as referências é essencial para manter os
elementos da lista conectados.
Para determinar os elementos
inicial e final da lista,

Lista Encadeada (cont.)


referências para esses
elementos são adicionadas à
ESTRUTURA DE DADOS classe da lista, sendo chamados
de HEADER/INÍCIO e
INÍCIO FINAL TRAILER/FINAL.

NULL NULL

5 8 7 10 2 3

O terceiro atributo de um nó Conforme apresentado, cada O segundo atributo de um nó


guarda a REFERÊNCIA NÓ possui três atributos. O guarda a REFERÊNCIA
(localização) para o nó primeiro armazena o elemento (localização) para o PRÓXIMO
ANTERIOR. (dado) que compõe a lista. nó.
Implementação
A implementação de listas, filas e
pilhas através do uso de
elementos duplamente
encadeados segue os mesmos
padrões das estruturas
encadeadas simples.
◦ As mudanças limitam-se aos
aspectos responsáveis por
adicionar e manipular as
referências para elementos
antecessores na estrutura.
DEVER DE CASA
Implementar as estruturas de
dados duplamente encadeadas dos
tipos lista, fila e pilha utilizando as
linguagens de programação Java e
C++.
◦ Recomenda-se a escrita inicial da
lógica central da estrutura
apresentada.
◦ Após a compreensão do código
escrito, adicionar os recursos
avançados da linguagem.
◦ Lembre-se de validar a sua estrutura
através da montagem de exemplos
que manipulam essas estruturas.
FUNDAÇÃO EDSON QUEIROZ
UNIVERSIDADE DE FORTALEZA
ENSINANDO E APRENDENDO

IMPLEMENTAÇÃO DE LISTAS
DUPLAMENTE ENCADEADAS

DEMONSTRAÇÃO
FUNDAÇÃO EDSON QUEIROZ
UNIVERSIDADE DE FORTALEZA
ENSINANDO E APRENDENDO

conclusão
RESUMO DOS TÓPICOS APRESENTADOS E
LINKS COMPLEMENTARES
Resumo

Introdução
Listas
Filas
Pilhas
Encadeamento
Conclusão
Obrigado!

Você também pode gostar