Capítulo 4
Listas Encadeadas
Listas Encadeadas Simples
4.1 Listas
Encadeadas
4.1.1 Modelagem
de Dados
4.1.2 Listas
Encadeadas: Modelagem de Dados Genérica
4.1.3 Modelagem
Funcional
4.0 Listas com Vetores: Desvantagens
-
Tamanho máximo fixo
-
Mesmo vazias ocupam espaço grande de
memória
-
Mesmo que utilizemos um vetor
de ponteiros, se quisermos prever uma lista de 10.000 elementos, teremos
40.000 bytes desperdiçados.
-
Operações podem involver muitos
deslocamentos de dados:
-
Inclusão em uma posição
ou no início
-
Exclusão em uma posição
ou no início
4.1
Listas Encadeadas
-
São listas onde cada
elemento contido em uma lista está armazenado em um TAD chamado
elemento de lista.
-
Cada elemento de lista referencia
o próximo e só é alocado dinamicamente quando é
necessário
-
Para referenciar o primeiro
elemento utilizamos um TAD cabeça de lista.
4.1.1
Modelagem de Dados
Cabeça de Lista
-
Necessitamos:
-
Um ponteiro para o primeiro
elemento da lista.
-
Um inteiro para indicar quantos
elementos a lista possue.
-
Pseudo-código:
tipo Lista {
Elemento *dados;
inteiro tamanho;
};
Elemento de Lista
Necessitamos:
-
Um ponteiro para o próximo
elemento da lista.
-
Um campo do tipo da informação
que vamos armazenar.
-
Pseudo-código:
tipo Elemento {
Elemento *próximo;
tipo-que-eu-vou-usar-nesta-aplicação
info;
};
4.1.2
Listas Encadeadas: Modelagem de Dados Genérica
-
Para tornar todos os algoritmos
da lista mais genéricos, fazemos o campo info ser um ponteiro para
um elemento de informação.
Modelagem: Elemento de Lista
II
tipo Elemento {
Elemento *próximo;
TipoInfo *info;
};
tipo TipoInfo {
tipo-do-campo1 campo1;
tipo-do-campo2 campo2;
?
tipo-do-campoN campoN;
}
-
Razões para a modelagem do TipoInfo:
-
Vamos na maioria dos algoritmos
trabalhar com algum elemento de infomação.
-
Se este elemento é somente
um ponteiro para um TipoInfo, não importando o que este seja, teremos
algoritmos totalmente genéricos.
-
Posso usar o mesmo código
de lista para muitas aplicações diferentes simplesmente recompilando.
-
Desvantagens:
-
O algoritmo de destruição
da lista torna-se mais complexo.
4.1.3
Modelagem Funcional
-
Aspecto Funcional: Três
Grupos de Funções
-
Colocar e retirar dados da lista.
-
Testar se a lista está vazia e outros
testes.
-
Inicializa-la e garantir a ordem dos elementos.
Modelagem da Lista
Operações: Colocar e retirar
dados da lista:
-
Adiciona(lista, dado)
-
AdicionaNoInício(lista, dado)
-
AdicionaNaPosição(lista, dado,
posição)
-
AdicionaEmOrdem(lista, dado)
-
Retira(lista)
-
RetiraDoInício(lista)
-
RetiraDaPosição(lista, posição)
-
RetiraEspecífico(lista, dado)
Operações: Testar a lista
e outros testes:
-
ListaVazia(lista)
-
Posicao(lista, dado)
-
Contem(lista, dado)
Operações:
Inicializar ou limpar:
-
CriaLista()
-
DestroiLista(lista)
Algoritmo
CriaLista
Lista* FUNÇÃO criaLista()
"Retorna ponteiro
para uma nova cabeça de lista ou NULO"
variáveis
Lista *aLista;
início
aLista <- aloque(Lista);
SE (aLista ~= NULO) ENTÃO
"So posso inicializar
se consegui alocar"
aLista->tamanho <- 0;
aLista->dados <- nulo;
FIM SE
RETORNE(aLista);
fim;
Algoritmo
ListaVazia
Booleano FUNÇÃO listaVazia(Lista
*aLista)
início
SE (aLista->tamanho = 0) ENTÃO
RETORNE(Verdade)
SENÃO
RETORNE(Falso);
fim;
-
Um algoritmo ListaCheia não
existe aqui.
-
Verificar se houve espaço
na memória para um novo elemento será responsabilidade de
cada operação de adição.
Algoritmo
AdicionaNoInício
-
Procedimento:
-
Testamos se é possível alocar
um elemento.
-
Fazemos o próximo deste novo elemento
ser o primeiro da lista.
-
Fazemos a cabeça de lista apontar para
o novo.
-
Parâmetros:
-
O tipo info (dado) a ser inserido.
-
Lista.

Inteiro FUNÇÃO adicionaNoInício(
Lista *aLista,
TipoInfo *dado )
variáveis
Elemento *novo; "Var. auxiliar para
o novo elemento"
início
novo <- aloque(Elemento);
SE (novo = NULO) ENTÃO
RETORNE( ErroListaCheia );
SENÃO
novo->proximo <- aLista->dados;
novo->info <- dado;
aLista->dados <- novo;
aLista->tamanho <- aLista->tamanho
+ 1;
RETORNE(1);
FIM SE
fim;
Algoritmo
RetiraDoInício
-
Procedimento:
-
Testamos se há elementos.
-
Decrementamos o tamanho.
-
Liberamos a memória do elemento.
-
Devolvemos a Informação.
-
Parâmetros:
TipoInfo* FUNÇÃO retiraDoInício(
Lista *aLista )
"Elimina o 1. Elemento de uma lista.
Retorna a informação
do elemento eliminado ou NULO."
variáveis
Elemento *saiu; "Var. auxiliar para
o 1. elemento"
TipoInfo *volta; "Var. auxiliar para
o dado retornado"
início
SE (listaVazia(aLista)) ENTÃO
RETORNE( NULO );
SENÃO
saiu <- aLista->dados;
volta <- saiu->info;
aLista->dados <- saiu->proximo;
aLista->tamanho <- aLista->tamanho
- 1;
LIBERE(saiu);
RETORNE(volta);
FIM SE
fim;
Algoritmo
EliminaDoInício
inteiro FUNÇÃO eliminaDoInício(
Lista *aLista )
"Elimina o 1. Elemento de uma lista
e sua respectiva informação.
Retorna a posição
do elemento eliminado ou erro."
variáveis
Elemento *saiu; "Var. auxiliar para
o 1. elemento"
início
SE (listaVazia(aLista)) ENTÃO
RETORNE( ErroListaVazia );
SENÃO
saiu <- aLista->dados;
aLista->dados <- saiu->proximo;
aLista->tamanho <- aLista->tamanho
- 1;
LIBERE(saiu->info);
LIBERE(saiu);
RETORNE(1);
FIM SE
fim;
-
Observe que a linha libere(saiu->info)
possui um perigo:
-
Se o tipoInfo for por sua vez
um conjunto estruturado de dados com referências internas através
de ponteiros (outra lista, p.ex.), a chamada à função
libere(saiu->info) só liberará o primeiro nível da
estrutura (aquele apontado diretamente).
-
Tudo o que for referenciado
através de ponteiros em info permanecerá em algum lugar da
memória, provavelmente inatingível (garbage).
-
Para evitar isto pode-se criar
uma função destroi(info) para o TipoInfo que será
chamada ao invés de libere.
Exemplo
simplificado: Programa Principal
#inclua listaEnc.h
variáveis
Lista *devedores,
*credores, *listaEscolhida;
TipoInfo *dado;
caracter opção;
1 Programa Principal
2 início
3
devedores <- criaLista();
4
credores <- criaLista();
5
opção <- ´´;
6
ENQUANTO (opção ~= ´f´) ENTÃO
7
escreveMenu();
8
leia(opção);
9
CASO opção SEJA
10
´c´: listaEscolhida <- credores;
11
´d´: listaEscolhida <- devedores;
12
´i´: dado <- leiaInfo(); "P/tipoInfo leitura espec."
13
adicionaNoInício(listaEscolhida,dado);
--
- - - -
--
- - - -
--
FIM CASO
--
FIM ENQUANTO
-- fim;
-
Memória logo após
o início do programa,
quando o fluxo de execução
do programa se encontra
na
linha #2.
|
 |
-
Memória logo após
as
2 chamadas às funções
criaLista(), quando o
fluxo de execução
do
programa se encontra
na linha #5.
|
 |
-
Memória imediatamente
antes de retornar de uma
chamada à função
adicionaNoInício(),
quando a listaEscolhida
é a dos credores
e o
fluxo de execução
do
programa se encontra
na última linha da
função
adicionaNoInício
e retornará
ao programa principal para
a
linha #13.
|
 |
Algoritmo
AdicionaNaPosição
-
Procedimento:
-
Testamos se a posição
existe e se é possível alocar elemento.
-
Caminhamos até a posição.
-
Adicionamos o novo dado na posição.
-
Incrementamos o tamanho.
-
Parâmetros:
-
O dado a ser inserido.
-
A posição onde
inserir.
-
Lista.
Inteiro FUNÇÃO adicionaNaPosição(
Lista *aLista, TipoInfo *info, inteiro posição )
"Adiciona novo elemento
na posição dada.
Retorna a novo numero de elementos
da lista ou erro."
variáveis
Elemento *novo, *anterior;
"Ponteiros auxiliares"
início
SE (posição > aLista->tamanho
+ 1) ENTÃO
RETORNE( ErroPosição
)
SENÃO
SE (posição = 1) ENTÃO
RETORNE(adicionaNoInício(aLista,
info);
SENÃO
novo <- aloque(Elemento);
SE (novo = NULO) ENTÃO
RETORNE( ErroListaCheia );
SENÃO
anterior <- aLista->dados;
REPITA (posição - 2)
VEZES
anterior <- anterior->proximo;
novo->proximo <- anterior->proximo;
novo->info <- info;
anterior->proximo <- novo;
aLista->tamanho <- aLista->tamanho
+ 1;
RETORNE(aLista->tamanho);
FIM SE
FIM SE
FIM SE
fim;
Algoritmo
RetiraDaPosição
-
Procedimento:
-
Testamos se a posição
existe.
-
Caminhamos até a posição.
-
Retiramos o dado da posição.
-
Decrementamos o tamanho.
-
Parâmetros:
-
A posição de onde
retirar.
-
Lista.
TipoInfo* FUNÇÃO
retiraDaPosição( Lista *aLista, inteiro posição
)
"Elimina o Elemento na
posição posição de uma lista.
Retorna a informação
do elemento eliminado ou NULO."
variáveis
Elemento *anterior, *eliminar;
"Var. auxiliar para elemento"
TipoInfo *volta; "Var. auxiliar para
o dado retornado"
início
SE (posição
> aLista->tamanho) ENTÃO
RETORNE( NULO );
SENÃO
SE (posição
= 1) ENTÃO
RETORNE( retiraDoInício(aLista) );
SENÃO
anterior <- aLista->dados;
REPITA (posição - 2) VEZES
anterior <- anterior->proximo;
eliminar <- anterior->proximo;
volta <- eliminar->info;
anterior->proximo <- eliminar->proximo;
aLista->tamanho <- aLista->tamanho - 1;
LIBERE(eliminar);
RETORNE(volta);
FIM SE
FIM SE
fim;
Modelagem
do Tipo Info
-
Para inserção em ordem e para
achar um elemento determinado, necessitamos da capacidade de comparar informações
associadas aos elementos.
-
Estas operações
de comparação fazem parte do TAD TipoInfo e não da
lista.
-
Devem ser implementadas como
tal.
-
Operações: Testar AS INFORMAÇÕES:
-
Igual(dado1,dado2)
-
Maior(dado1,dado2)
-
Menor(dado1,dado2)
Algoritmo
AdicionaEmOrdem
-
Procedimento:
-
Necessitamos de uma função para
comparar os dados (maior)
-
Procuramos pela posição onde
inserir comparando dados.
-
Chamamos adicionaNaPosição.
-
Parâmetros:
-
O dado a ser inserido.
-
Lista.
Inteiro FUNÇÃO adicionaEmOrdem(Lista
*aLista, TipoInfo dado)
variáveis
Elemento *atual;
"Variável auxiliar para caminhar"
inteiro posição;
início
SE (listaVazia(aLista)) ENTÃO
RETORNE(adicionaNoInício(aLista,
dado));
SENÃO
atual <- aLista->dados;
posição
<- 1;
ENQUANTO (atual->proximo
~= NULO E maior(dado, atual->info)) FAÇA
"Encontrar posição para inserir"
atual <- atual->proximo;
posição <- posição + 1;
FIM ENQUANTO
SE maior(dado, atual->info)
ENTÃO "Parou porque acabou lista"
RETORNE(adicionaNaPosição(aLista, dado, posição
+ 1));
SENÃO
RETORNE(adicionaNaPosição(aLista, dado, posição));
FIM SE
FIM SE
fim;
Algoritmo
DestroiLista
FUNÇÃO destroiLista(Lista
*aLista)
variáveis
Elemento *atual, *anterior; "Variável
auxiliar para caminhar"
início
SE (listaVazia(aLista)) ENTÃO
LIBERE(aLista);
SENÃO
atual <- aLista->dados;
ENQUANTO (atual ~= NULO) FAÇA
"Eliminar até o fim"
anterior <- atual;
"Vou para o próximo mesmo que
seja nulo."
atual <- atual->proximo;
"Liberar primeiro a Info"
LIBERE(anterior->info);
"Liberar o elemento que acabei de visitar."
LIBERE(anterior);
FIM ENQUANTO
LIBERE(aLista);
FIM SE
fim;
Algoritmos
Restantes
-
Por conta do Aluno:
-
Adiciona(lista, dado)
-
Retira(lista)
-
RetiraEspecífico(lista, dado)