2. 2
Listas Estáticas Encadeadas
Vamos apresentar uma outra possibilidade de
implementação do TAD Listas.
Essa implementação é chamada de estática
encadeada
◼ Estática por utilizar vetores;
◼ Encadeada por manter uma estrutura de
encadeamento que difere da estrutura seqüencial.
Essa implementação é menos comum do que as
demais, mas bastante interessante por motivos
didáticos.
3. 3
Listas Estáticas Encadeadas
Listas Estáticas Encadeadas simulam o conceito
de ponteiros:
◼ Esses “ponteiros” armazenam posições do vetor ao
invés de endereços de memória;
◼ Chamamos esses “ponteiros” de cursores.
A princípio, todas as posições do vetor estão
disponíveis, isso é representado da seguinte
maneira usando o cursor dispo em listas estáticas
simplesmente encadeadas...
4. 4
Lista Dispo
2
?
1 2 3 4
Primeiro: -1 Dispo: 1
3
? 4
? 5
? -1
?
5
Legenda:
Chave Prox
A operação de criar a lista
é responsável por realizar
essa inicialização.
Qual é a complexidade
dessa operação?
5. 5
Lista Dispo
2
?
1 2 3 4
Primeiro: -1 Dispo: 1
3
? 4
? 5
? -1
?
5
Legenda:
Chave Prox
A operação de criar a lista
é responsável por realizar
essa inicialização.
Qual é a complexidade
dessa operação?
6. 6
Lista Dispo
2
?
1 2 3 4
Primeiro: -1 Dispo: 1
3
? 4
? 5
? -1
?
5
Legenda:
Chave Prox
A operação de criar a lista
é responsável por realizar
essa inicialização.
Qual é a complexidade
dessa operação? O(n).
7. 7
Definição de Tipos
#define MAX_TAM 300
#define NAO_ENCONTRADO -1
typedef int TipoChave;
typedef struct {
TipoChave chave;
/* Outros Campos */
} TipoItem;
typedef struct {
TipoItem item;
int prox;
} TipoNo;
11. 11
Cria lista
/* Pré-condição: nenhuma;
Pós-condição: retorna uma lista
vazia. */
TipoLista IniciaLista() {
int i;
TipoLista L = malloc(sizeof(struct
TipoLista));
if(L != NULL) {
L->primeiro = -1;
L->dispo = 0; /* segue... */
12. 12
Cria lista
for(i = 0; i < MAX_TAM - 1; i++) {
L->no[i].prox = i + 1;
}
assert(i == MAX_TAM - 1); /* não é
realmente necessária. Útil para
debug */
L->no[i].prox = -1;
}
return L;
}
13. 13
assert(<exp> )
assert(), definida em assert.h, aborta a
execução do programa se a expressão
<exp> vale 0 (zero). Caso contrário,
assert() não faz nada
Caso <exp> valer 0 (zero), escreve
informação do erro -- o formato dessa
informação depende do compilador
14. 14
assert(<exp> )
assert() geralmente é usada para ajudar a
verificar se um programa está operando
corretamente. A expressão <exp> deve
ser planejada de forma a que ela seja
verdadeira (diferente de 0) somente
quando não ocorre nenhum erro
Não é necessário remover assert() do
código fonte após o programa ter sido
depurado
15. 15
Destroi lista
/* Pré-condição: a lista já está
iniciada;
Pós-condição: libera memória
ocupada pela lista. */
void DestroiLista(TipoLista L) {
free(L);
}
16. 16
Inserção na primeira posição
A operação de inserção deve ser realizada
em dois passos:
◼ Recuperar a posição disponível;
◼ Inserir o elemento, atualizando os cursores
primeiro e prox.
Vamos simular a inserção do valor 7...
17. 17
Inserção na primeira posição
2
?
1 2 3 4
Primeiro: -1 Dispo: 1
3
? 4
? 5
? -1
?
5
Legenda:
Chave Prox
Inserção do valor 7 no
início da lista (vazia).
Qual é a complexidade da
operação de inserção no
início da lista?
18. 18
Inserção na primeira posição
2
?
1 2 3 4
Primeiro: 1 Dispo: 1
3
? 4
? 5
? -1
?
5
Legenda:
Chave Prox
Inserção do valor 7 no
início da lista (vazia)..
Qual é a complexidade da
operação de inserção no
início da lista?
19. 19
Inserção na primeira posição
2
?
1 2 3 4
Primeiro: 1 Dispo: 2
3
? 4
? 5
? -1
?
5
Legenda:
Chave Prox
Inserção do valor 7 no
início da lista (vazia).
Qual é a complexidade da
operação de inserção no
início da lista?
20. 20
Inserção na primeira posição
-1
7
1 2 3 4
Primeiro: 1 Dispo: 2
3
? 4
? 5
? -1
?
5
Legenda:
Chave Prox
Inserção do valor 7 no
início da lista (vazia).
Qual é a complexidade da
operação de inserção no
início da lista?
21. 21
Inserção na primeira posição
-1
7
1 2 3 4
Primeiro: 1 Dispo: 2
3
? 4
? 5
? -1
?
5
Legenda:
Chave Prox
Inserção do valor 7 no
início da lista (vazia).
Qual é a complexidade da
operação de inserção no
início da lista? O(1)
22. 22
Inserção na primeira posição
-1
7
1 2 3 4
Primeiro: 1 Dispo: 2
3
? 4
? 5
? -1
?
5
Legenda:
Chave Prox
Inserção de um segundo
valor, 4, no início da lista
(não vazia).
23. 23
Inserção na primeira posição
-1
7
1 2 3 4
Primeiro: 2 Dispo: 3
1
4 4
? 5
? -1
?
5
Legenda:
Chave Prox
Inserção de um segundo
valor, 4, no início da lista
(não vazia).
24. 24
Inserção na primeira posição
-1
7
1 2 3 4
Primeiro: 2 Dispo: 3
1
4 4
? 5
? -1
?
5
Legenda:
Chave Prox
Inserção de um segundo
valor, 4, no início da lista
(não vazia).
Qual é a complexidade da
operação de inserção no
início da lista com n
elementos? O(1)
25. 25
Inserção na primeira posição
Uma situação especial é a inserção de um
elemento quando a lista somente possui
uma posição livre.
Vamos ver um exemplo disso...
26. 26
Inserção na primeira posição
-1
7
1 2 3 4
Primeiro: 4 Dispo: 5
1
4 2
3 3
9 -1
?
5
Legenda:
Chave Prox
Inserção do valor 3
quando somente existe
uma posição livre.
27. 27
Inserção na primeira posição
-1
7
1 2 3 4
Primeiro: 5 Dispo: -1
1
4 2
3 3
9 4
3
5
Legenda:
Chave Prox
Inserção do valor 3
quando somente existe
uma posição livre.
28. 28
Inserção na primeira posição
-1
7
1 2 3 4
Primeiro: 5 Dispo: -1
1
4 2
3 3
9 4
3
5
Legenda:
Chave Prox
Inserção do valor 3
quando somente existe
uma posição livre.
29. 29
Lista Cheia e Vazia
Dessa forma, podemos estipular regras
para verificar se uma lista está cheia ou
vazia:
◼ Lista Vazia: Primeiro = -1;
◼ Lista Cheia: Dispo = -1.
30. 30
Lista cheia
/* Pré-condição: a lista já está
iniciada;
Pós-condição: retorna 1 se a
lista estiver cheia e 0 caso-
contrário. */
int ListaCheia(TipoLista L) {
.............
}
31. 31
Lista cheia
/* Pré-condição: a lista já está
iniciada;
Pós-condição: retorna 1 se a
lista estiver cheia e 0 caso-
contrário. */
int ListaCheia(TipoLista L) {
if(L->dispo == -1) {
return 1;
} else {
return 0;
}
}
32. 32
Lista vazia
/* Pré-condição: a lista já está
iniciada;
Pós-condição: retorna 1 se a
lista estiver vazia e 0 caso-
contrário. */
int ListaVazia(TipoLista L) {
.............
}
33. 33
Lista vazia
/* Pré-condição: a lista já está
iniciada;
Pós-condição: retorna 1 se a
lista estiver vazia e 0 caso-
contrário. */
int ListaVazia(TipoLista L) {
if(L->primeiro == -1) {
return 1;
} else {
return 0;
}
}
34. 34
Inserir elemento (primeira
posição)
/* Pré-condição: a lista já está iniciada
e não está cheia;
Pós-condição: insere um elemento na
primeira posição. */
void InserePrimeiroLista(TipoLista L,
TipoItem elem) {
.................
}
35. 35
Inserir elemento (primeira
posição)
/* Pré-condição: a lista já está iniciada
e não está cheia;
Pós-condição: insere um elemento na
primeira posição. */
void InserePrimeiroLista(TipoLista L,
TipoItem elem) {
int dispo = L->dispo;
L->no[dispo].item = elem;
L->dispo = L->no[dispo].prox;
L->no[dispo].prox = L->primeiro;
L->primeiro = dispo;
}
36. 36
Inserção na última posição
3
7
1 2 3 4
Primeiro: 2 Dispo: 4
1
4 -1
10 5
? -1
?
5
Legenda:
Chave Prox
Deve percorrer a lista
para achar o último
elemento, inserir após ele
o novo elemento (seja 12)
e atualizar a estrutura
37. 37
Inserção na última posição
3
7
1 2 3 4
Primeiro: 2 Dispo: 5
1
4 4
10 -1
12 -1
?
5
Legenda:
Chave Prox
Após inserir o novo
elemento 12
38. 38
Inserir elemento (última posição)
/* Pré-condição: a lista já está iniciada
e não está cheia;
Pós-condição: insere um elemento na
última posição. */
void InsereUltimoLista(TipoLista L,
TipoItem elem) {
.................
}
39. 39
Inserir elemento (última posição)
/* Pré-condição: a lista já está iniciada
e não está cheia;
Pós-condição: insere um elemento na
última posição. */
void InsereUltimoLista(TipoLista L,
TipoItem elem) {
int dispo = L->dispo;
int i = L->primeiro;
L->no[dispo].item = elem;
L->dispo = L->no[dispo].prox;
L->no[dispo].prox = -1; /* Segue... */
40. 40
Inserir elemento (última posição)
if(i == -1) {/* lista de entrada vazia*/
L->primeiro = dispo;
} else {
do {
if(L->no[i].prox == -1) {/* ultimo
elemento da lista de entrada */
L->no[i].prox = dispo;
i = -1; /* para fim do loop*/
} else {
i = L->no[i].prox;
}
} while(i != -1);
}
}
41. 41
Observação
-1
?
1 2 3 4
Primeiro: 3 Dispo: 1
5
4 2
3 -1
9 4
7
5
Legenda:
Chave Prox
Esta instância da
estrutura representa a
lista (3, 4, 7, 9)
42. 42
Observação
5
3
1 2 3 4
Primeiro: 1 Dispo: 4
3
7 -1
9 -1
? 2
4
5
Legenda:
Chave Prox
Essa lista (3, 4, 7, 9)
também é representada
por esta outra instância
da estrutura...
43. 43
Observação
Nessa lista (3, 4, 7, 9) convencionamos que o
elemento 3 está na posição 0, 4 na
posição 1, 7 na posição 2 e assim por
diante
44. 44
Observação
Após diversas inserções e remoções a lista
não estará com os elementos na seqüência
das posições do vetor.
Dessa forma, a pesquisa deve seguir a
partir do cursor Primeiro (elemento na
posição 0) e seguir os cursores Prox de
cada elemento incrementando em 1 o valor
da posição do elemento “visitado”.
45. 45
Pesquisa
-1
?
1 2 3 4
Primeiro: 3 Dispo: 1
5
4 2
3 -1
9 4
7
5
Legenda:
Chave Prox
Pesquisar por todos os
elementos na lista
49. 49
Pesquisa
-1
?
1 2 3 4
Primeiro: 3 Dispo: 1
5
4 2
3 -1
9 4
7
5
Legenda:
Chave Prox
Pesquisa pela chave 4.
Deve retornar a posição.
Qual é a complexidade
dessa operação?
Aux: 2
50. 50
Pesquisa
-1
?
1 2 3 4
Primeiro: 3 Dispo: 1
5
4 2
3 -1
9 4
7
5
Legenda:
Chave Prox
Pesquisa pela chave 4.
Qual é a complexidade
dessa operação? O(n).
Aux: 2
51. 51
Pesquisar elemento (por posição)
/* Pré-condição: a lista já está iniciada;
Pós-condição: retorna a posição do
elemento na lista, ou um valor especial
caso não exista. */
int PesquisaLista(TipoLista L, TipoChave
chave) {
................
}
52. 52
Pesquisar elemento (por posição)
/* Pré-condição: a lista já está iniciada;
Pós-condição: retorna a posição do elemento na
lista, ou um valor especial caso não exista. */
int PesquisaLista(TipoLista L, TipoChave chave) {
int i = L->primeiro;
int achou = 0;
int posAtual = 0;
while((!achou) && (i != -1)) {
if(L->no[i].item.chave == chave) {
achou = 1;
} else {
i = L->no[i].prox;
posAtual++;
}
} /* segue... */
54. 54
Remoção (por posição)
A remoção pode ser realizada por posição,
ou seja, um cursor para uma posição do
vetor deve ser informado.
Entretanto, é necessário que se conheça o
elemento anterior.
Dessa forma, a remoção é bastante similar
a realizada em listas ligadas.
É importante colocar o elemento liberado
na lista Dispo.
55. 55
Remoção (por posição)
-1
?
1 2 3 4
Primeiro: 3 Dispo: 1
5
14 2
3 -1
9 4
7
5
Legenda:
Chave Prox
Lista (3, 14, 7, 9)
Remoção do elemento na
posição 2 (i.e. o 3ro
elemento da lista que é o
elemento 7)
59. 59
Remoção (por posição)
-1
?
1 2 3 4
Primeiro: 3 Dispo: 1
4
14 2
3 -1
9 4
7
5
Legenda:
Chave Prox
Lista (3, 14, 7, 9)
Remoção do elemento na
posição 2 (elemento 7)
Atualização da lista
encadeada e...
Aux: 2
60. 60
Remoção (por posição)
-1
?
1 2 3 4
Primeiro: 3 Dispo: 5
4
14 2
3 -1
9 1
?
5
Legenda:
Chave Prox
Remoção do elemento na
posição 2
Atualização da lista
encadeada e...
Atualização da lista Dispo
Aux: 2
61. 61
Remoção (por posição)
-1
?
1 2 3 4
Primeiro: 3 Dispo: 5
4
14 2
3 -1
9 1
?
5
Legenda:
Chave Prox
Lista retornada (3, 14, 9)
Qual é a complexidade
essa operação? O(n)
62. 62
Remover elemento (por posição)
/* Pré-condição: a lista já está
iniciada e uma posição válida da lista é
informada;
Pós-condição: o elemento na posição
fornecida é removido da lista. */
void RemoveDeLista(TipoLista L, int pos) {
..............
}
63. 63
Remover elemento (por posição)
/* Pré-condição: a lista já está iniciada e
uma posição válida da lista é informada;
Pós-condição: o elemento na posição fornecida é
removido da lista. */
void RemoveDeLista(TipoLista L, int pos) {
int i;
int anterior;
int posAtual;
if(pos == 0) {/* remover 1ro elemento da lista*/
i = L->primeiro;
L->primeiro = L->no[L->primeiro].prox;
L->no[i].prox = L->dispo;
L->dispo = i;
} /* Segue...*/
65. 65
TAD Lista
A seguir veremos a implementação do
TAD lista mas usando agora uma
estrutura estática na qual os
elementos da lista encontram-se
duplamente encadeados
66. 66
Listas Duplamente Encadeadas
Os cursores na estrutura estática podem
ser utilizados para implementar listas
duplamente encadeadas.
Para isso basta manter um ponteiro ant
para um elemento anterior.
Variáveis auxiliares como nr_elementos e
ultimo ajudam nas operações de contar e
inserir na última posição, respectivamente,
e serão usadas nesta implementação.
68. 68
Definição de Tipos
#define MAX_TAM 3
#define NAO_ENCONTRADO -1
typedef int TipoChave;
typedef struct {
TipoChave chave;
/* Outros Campos */
} TipoItem;
typedef struct {
TipoItem item;
int ant;
int prox;
} TipoNo;
69. 69
Definição de Tipos
typedef struct TipoLista {
TipoNo no[MAX_TAM];
int primeiro;
int dispo;
int nrElementos;
int ultimo;
} *TipoLista;
70. 70
Listas Duplamente Encadeadas
2
?
? 3
?
? 4
?
? -1
?
?
1 2 3 4
Primeiro: -1 NrElementos: 0 Ultimo: -1 Dispo: 1
Ant Chave Prox
Legenda: Inserir o valor 2 no início da lista.
O que deve ser feito?
71. 71
Listas Duplamente Encadeadas
-1
2
-1 3
?
? 4
?
? -1
?
?
1 2 3 4
Primeiro: 1 NrElementos: 1 Ultimo: 1 Dispo: 2
Ant Chave Prox
Legenda:
Após a inserção do valor 2
no início da lista
72. 72
Listas Duplamente Encadeadas
-1
2
-1 3
?
? 4
?
? -1
?
?
1 2 3 4
Primeiro: 1 NrElementos: 1 Ultimo: 1 Dispo: 2
Ant Chave Prox
Legenda:
Inserir o valor 4 no fim da lista.
O que deve ser feito?
73. 73
Listas Duplamente Encadeadas
2
2
-1 -1
4
1 4
?
? -1
?
?
1 2 3 4
Primeiro: 1 NrElementos: 2 Ultimo: 2 Dispo: 3
Ant Chave Prox
Legenda:
Após a inserção do valor 4
no final da lista
74. 74
Análise
A implementação estática encadeada compartilha
características comuns com as implementações
de listas com vetores e com ponteiros:
◼ Vetores:
O número máximo de elementos deve ser
estipulado a priori.
◼ Ponteiros:
As operações de inserção no início e em qualquer
posição e remoção em qualquer posição não
requerem o deslocamento de elementos.
75. 75
Exercício (usando as
declarações de tipo definidas)
1. Implemente as operações do TAD lista
utilizando uma lista estática encadeada
simples.
2. Implemente as operações do TAD lista
utilizando uma lista estática encadeada
dupla.
3. Qual a complexidade dessas operações
utilizando lista estática encadeada
simples e dupla?