Listas Encadeadas

Propaganda
Estruturas de Dados
Listas Dinâmicas
Simplesmente
Encadeadas
Prof. Ricardo J. G. B. Campello
Créditos
Parte dos slides a seguir são adaptações,
extensões e recodificações em C dos originais:
disponíveis em http://ww3.datastructures.net/
cedidos pela Profa. Maria Cristina F. de Oliveira
2
1
Listas Dinâmicas
Utiliza alocação dinâmica de memória ao
invés de arranjos (vetores) pré-alocados.
Lista dispõe de toda memória disponível para o
programa durante a execução (heap)
Alocação/liberação desses endereços é
gerenciada pelo S.O., por meio de comandos
da linguagem de programação
Linguagem C: malloc e free
3
Listas Simplesmente Encadeadas
Uma lista encadeada é uma
estrutura de dados linear que
consiste de uma seqüência
interligada de nós (ou nodos)
dinamicamente alocados.
próximo
No caso de encadeamento
simples, cada nodo armazena:
elemento (registro, objeto, ...)
ponteiro para o próximo nodo.
elemento
nodo
∅
A
B
C
D
4
2
Listas Simplesmente Encadeadas
nodo
Nodo:
lig
elem
struct list_node {
tipo_elem
struct list_node
};
typedef struct list_node
elem;
*lig;
nodo;
5
Listas Simplesmente Encadeadas
nulo (NULL)
Lista:
typedef struct {
int nelem;
nodo *head, *tail;
} Lista;
Lista L;
/* Exemplo de Declaração */
6
3
Listas Simplesmente Encadeadas
Inicialização:
nulo
(NULL)
Lista *Definir(void){
Lista *L;
L = malloc(sizeof(Lista));
L->nelem = 0;
L->head = NULL;
L->tail = NULL;
return L;
}
7
Inserindo no Início
1. Aloque o novo nodo.
2. Insira o novo elemento.
3. Aponte o novo nodo
para head.
4. Aponte head para o
novo nodo.
PS. símbolo ∅ representa NULL.
8
4
Inserindo no Início
nodo *Inserir_frente(tipo_elem x, Lista *L){
nodo *Pa;
Pa = malloc(sizeof(nodo));
Pa->elem = x;
Pa->lig = L->head;
L->head = Pa;
if (L->tail == NULL)
/* L antes vazia */
L->tail = L->head;
L->nelem++;
return Pa;
/* O(1) */
}
9
Removendo do Início
1. Aponte um ponteiro
auxiliar Pa para head
2. Aponte head para o
próximo nodo.
3. Desaloque o primeiro
nodo usando Pa
10
5
Removendo do Início
tipo_elem Remover_frente(Lista *L){
tipo_elem x;
nodo *Pa;
Pa = L->head;
L->head = Pa->lig;
if (L->head == NULL)
/* L antes com elemento único */
L->tail = NULL;
x = Pa->elem;
free(Pa);
L->nelem--;
return x;
/* O(1) */
}
11
Inserindo no Final
1. Aloque o novo nodo.
2. Insira o novo elemento.
3. Faça o novo nodo
apontar para NULL.
4. Faça o antigo último
nodo apontar para o
novo.
5. Atualize tail para o novo
nodo.
Podemos inverter a ordem
dos passos 4 e 5 ?
12
6
Inserindo no Final
nodo *Inserir_final(tipo_elem x, Lista *L){
nodo *Pa;
Pa = malloc(sizeof(nodo));
Pa->elem = x;
Pa->lig = NULL;
if (L->head == NULL) L->head = Pa;
/* L antes vazia */
else (L->tail)->lig = Pa;
L->tail = Pa;
L->nelem++;
return Pa;
/* O(1) */
}
13
Removendo do Final
Remover no final de uma lista
simplesmente encadeada não
é eficiente.
Não existe algoritmo de
complexidade (tempo)
constante para acessar o
nodo anterior a tail.
A obtenção a partir de head é
O(n), onde n é o número de
elementos na lista (nelem na
nossa implementação).
14
7
Removendo do Final
tipo_elem Remover_final(Lista *L){
tipo_elem x;
nodo *Pa;
Pa = L->head;
if ((L->head)->lig == NULL) {
/* L com 1 elemento */
L->tail = NULL;
L->head = NULL; }
else {
while (Pa->lig != L->tail) Pa = Pa->lig;
L->tail = Pa;
Pa = Pa->lig;
(L->tail)->lig = NULL; }
x = Pa->elem; free(Pa);
L->nelem--;
return x; }
/* O(L.nelem) */
Exercícios
1.
Implemente uma função Lista_vazia(Lista *L) que retorna
True ou False se a lista está vazia ou não, respectivamente
2.
Seja o tipo dos elementos da lista, tipo_elem, definido como
um registro (struct) com dois campos: chave e info
1.a. Implemente a seguinte função:
retorno Localizar(tipo_elem x, Lista *L);
que retorna um registro do tipo retorno, também com dois
campos:
Campo 1 (int): Rank do elemento com chave x.chave na lista L
Campo 2 (nodo*): Ponteiro para o nodo de x em L
1.b. Qual o tempo de execução assintótico (BIG-O) de pior caso?
3.
Faz sentido aqui Localizar via Busca Binária? Justifique
8
Exercícios
4.
Implemente funções para remover elementos quaisquer da lista:
tipo_elem Remover_elem(tipo_elem x, Lista *L);
tipo_elem Remover_rank(int p, Lista *L);
A 1a deve remover da lista o nodo que armazena o elemento com
chave x.chave (retornando ao sistema a respectiva memória) e
retornar esse elemento
A 2a deve fazer o mesmo, mas para o p-ésimo elemento da lista
5.
Repita o exercício anterior para as funções Buscar_elem e
Buscar_rank, que apenas retornam o elemento, sem remover
o respectivo nodo da lista
6.
Implemente uma função Modificar que substitua o elemento
em uma dada colocação (rank) da lista por outro, retornando-o
17
Exercícios
7.
Implemente uma função Modify que substitua um dado
elemento x com chave x.chave por outro y também dado.
Use Localizar do exercício 1 e Modificar do exercício 5
8.
Suponha que se disponha de um ponteiro para um dado nodo
da lista (por exemplo obtido via Localizar do exercício 1):
9.
Explique porque, mesmo com esse ponteiro em mãos, não
é possível remover o respectivo nodo em tempo constante
O(1) (sem ter que percorrer a lista a partir de head)
É possível modificar a ED dos nodos para tornar isso
possível? Justifique
Faça um procedimento Esvaziar(Lista *L) que devolva a
memória de todos os nodos de uma lista L ao sistema e
reinicialize a lista
18
9
Bibliografia
A. M. Tenembaum et al., Data Structures Using C,
Prentice-Hall, 1990
M. T. Goodrich & R. Tamassia, Data Structures and
Algorithms in C++/Java, John Wiley & Sons,
2002/2005
N. Ziviani, Projeto de Algoritmos, Thomson, 2a.
Edição, 2004
J. L. Szwarcfiter & L. Markenzon, Estruturas de
Dados e seus Algoritmos, LTC, 1994
Schildt, H. "C Completo e Total", 3a. Edição,
Pearson, 1997
19
10
Download