• Nenhum resultado encontrado

Listas Encadeadas

N/A
N/A
Protected

Academic year: 2022

Share "Listas Encadeadas"

Copied!
58
0
0

Texto

(1)

Listas Encadeadas

Prof. Rafael Mesquita

rgm@cin.ufpe.br

(2)

O que veremos nesta aula?

Motivação

Estruturas de dados estáticas vs. dinâmicas

Limitações no uso de vetores

Listas encadeadas simples

Características

Definição da Estrutura

Operações básicas

Vantagens e desvantagens

Exercícios

(3)

Motivação

Vetores

int meuVetor[10];

Limitações?

Tamanho fixo:

quantidade de elementos não pode ser maior que a quantidade declarada

Disperdício de memória

Caso todas as posições não sejam utilizadas

(4)

Motivação

Vetores

Alocação dinâmica resolve o problema?

int* meuVetor = (int*) malloc(n*sizeof(int));

 Não completamente!!

Uma vez que a memória é alocada, os mesmos

problemas de limitação de espaço e disperdício

de memória existirão!

(5)

Motivação

Considere o seguinte vetor de float

como você faria para inserir ou remover um elemento em uma posição

específica, mantendo a ordem crescente?

Por exemplo, inserir 2.5...

Ou remover o elemento 3...

Problema: elementos posteriores precisam ser reorganizados

1 2 3 4.5 7.6

(6)

Lista encadeada

Resolve os problemas mencionados nos slides anteriores

Maior flexibilidade que vetores

Conjunto de dados pode crescer ou diminuir

Evita o disperdício de memória

Elementos podem ser inseridos ou removidos em posições específicas

Sem necessidade de reordenação do restante

dos elementos

(7)

Lista encadeada

Características principais

Estrutura sequencial

Cada elemento (ou nó) da lista possui

Um campo ‘informação’

Um campo ponteiro para o próximo elemento

info 1

p ro x p ro x

info 2

info 3

ref

(8)

Lista encadeada

Características principais

Caso não exista um próximo elemento, ponteiro

‘prox’ deve ser NULL

Indica o fim da lista

Existe, ainda, um ponteiro externo (‘ref’) que aponta para o primeiro nó da lista

‘ref’ é do tipo ponteiro para lista (ou para um nó da lista)

info 1

p ro x p ro x

info 2

info 3

ref

(9)

Lista encadeada

Implementação: novo tipo de dado struct lista{

int info;

struct lista* prox;

};

typedef struct lista Lista;

info

p ro x

(10)

Lista encadeada

Implementação: criação de uma nova lista

Lista* criaLista() {

return NULL;

}

Quando a lista é criada, temos uma lista vazia:

Não existe nenhum elemento

A referência para a lista aponta para NULL

Garante que o último elemento da lista aponta para NULL

int main() {

Lista* ref = criaLista();

...

}

(11)

Lista encadeada

Implementação: Inserção

Função deve receber a lista onde o novo elemento será inserido

E o valor da informação do novo elemento

Um inteiro no nosso exemplo

Abordagem utilizada nesta aula:

Inserir sempre no início da lista

Mais eficiente do que inserir no meio ou no final

da lista

(12)

Lista encadeada

Implementação: inserção no início da lista

info 1

p ro x p ro x

info 2

info 3

Lista* ref

(13)

Lista encadeada

Implementação: inserção no início da lista

info 1

p ro x p ro x

info 2

info 3

Lista* ref

info 0

p ro x

Novo

elemento inserido!

Lista* insere(Lista* ref, int valor) {

Lista* novoNo =

(Lista*)malloc(sizeof(Lista));

novoNo->info = valor;

novoNo->prox = ref;

ref = novoNo;

return ref;

}

(14)

Lista* insere(Lista* ref, int valor) {

Lista* novoNo = (Lista*)malloc(sizeof(Lista));

novoNo->info = valor;

novoNo->prox = ref; ref = novoNo;

return ref;

Lista encadeada

Detalhe: cópia de ‘ref’ é modificada em insere()

1 Lista* ref

int main() {

Lista* ref = NULL;

ref = insere(ref, 1);

...

NULL

ref é uma cópia de ref

(15)

Lista encadeada

Implementação: Impressão

Após inserção de elementos, é importante imprimir a lista para testar se o

funcionamento está correto

Mesmo vale após remoção de elementos

Cada nó da lista deve ser acessado, e sua

informação impressa

(16)

Lista encadeada

Implementação: impressão da lista

info 1

p ro x p ro x

info 2

info 3

Lista* ref

info 0

p ro x

p

void imprimir(Lista* ref) {

Lista* p;

printf("\nImprimindo a lista:\n");

if (ref == NULL){

printf("Lista vazia!");

return;

}

for (p = ref; p != NULL; p = p-

>prox)

printf("%d ", p->info);

}

(17)

void imprimir(Lista* ref) {

Lista* p;

printf("\nImprimindo a lista:\n");

if (ref == NULL){

printf("Lista vazia!");

return;

}

for (p = ref; p != NULL; p = p-

>prox)

printf("%d ", p->info);

}

Lista encadeada

Implementação: impressão da lista

info 1

p ro x p ro x

info 2

info 3

Lista* ref

info 0

p ro x

p

(18)

void imprimir(Lista* ref) {

Lista* p;

printf("\nImprimindo a lista:\n");

if (ref == NULL){

printf("Lista vazia!");

return;

}

for (p = ref; p != NULL; p = p-

>prox)

printf("%d ", p->info);

}

Lista encadeada

Implementação: impressão da lista

info 1

p ro x p ro x

info 2

info 3

Lista* ref

info 0

p ro x

p

(19)

void imprimir(Lista* ref) {

Lista* p;

printf("\nImprimindo a lista:\n");

if (ref == NULL){

printf("Lista vazia!");

return;

}

for (p = ref; p != NULL; p = p-

>prox)

printf("%d ", p->info);

}

Lista encadeada

Implementação: impressão da lista

info 1

p ro x p ro x

info 2

info 3

Lista* ref

info 0

p ro x

p

(20)

void imprimir(Lista* ref) {

Lista* p;

printf("\nImprimindo a lista:\n");

if (ref == NULL){

printf("Lista vazia!");

return;

}

for (p = ref; p != NULL; p = p-

>prox)

printf("%d ", p->info);

}

Lista encadeada

Implementação: impressão da lista

info 1

p ro x p ro x

info 2

info 3

Lista* ref

info 0

p ro x

p

p

(21)

Lista encadeada

Implementação: impressão da lista

info 1

p ro x p ro x

info 2

info 3

Lista* ref

info 0

p ro x

p

void imprimir(Lista* ref) {

Lista* p;

printf("\nImprimindo a lista:\n");

if (ref == NULL){

printf("Lista vazia!");

return;

}

for (p = ref; p != NULL; p = p-

>prox)

printf("%d ", p->info);

}

(22)

Lista encadeada

Implementação: impressão da lista

info 1

p ro x p ro x

info 2

info 3

Lista* ref

info 0

p ro x

p

void imprimir(Lista* ref) {

Lista* p;

printf("\nImprimindo a lista:\n");

if (ref == NULL){

printf("Lista vazia!");

return;

}

for (p = ref; p != NULL; p = p-

>prox)

printf("%d ", p->info);

}

(23)

void imprimir(Lista* ref) {

Lista* p;

printf("\nImprimindo a lista:\n");

if (ref == NULL){

printf("Lista vazia!");

return;

}

for (p = ref; p != NULL; p = p-

>prox)

printf("%d ", p->info);

}

Lista encadeada

Implementação: impressão da lista

info 1

p ro x p ro x

info 2

info 3

Lista* ref

info 0

p ro x

p

p =

NULL

(24)

void imprimir(Lista* ref) {

Lista* p;

printf("\nImprimindo a lista:\n");

if (ref == NULL){

printf("Lista vazia!");

return;

}

for (p = ref; p != NULL; p = p-

>prox)

printf("%d ", p->info);

}

Lista encadeada

Implementação: impressão da lista

info 1

p ro x p ro x

info 2

info 3

Lista* ref

info 0

p ro x

p =

NULL

(25)

Lista encadeada

Remoção de elementos

Função deve buscar um determinado elemento para ser removido

Ponteiro ‘p’ percorre a lista até que o

elemento a ser removido seja encontrado

Suponha a seguinte tentativa de remoção do nó

‘p’

Como acessar o nó 3 se a sua referência foi perdida?

2

p ro x p ro x

3 4

Lista* ref

1

p ro x

p

(26)

Lista encadeada

Remoção de elementos

Função deve buscar um determinado elemento para ser removido

Ponteiro ‘p’ percorre a lista até que o

elemento a ser removido seja encontrado

Solução para problema da referência:

Uso de ponteiro auxiliar ‘ant’, que também percorre a lista

‘ant’ deve estar sempre em uma posição anterior

a ‘p’

(27)

Lista encadeada

Remoção de elementos

Ex: Remover elemento com valor 3

2

p ro x p ro x

3 4

Lista* ref

1

p ro x

Lista* remove(Lista* ref, int valor)

{

Lista* ant=NULL;

Lista* p = ref;

p

ant = NULL

(28)

Lista encadeada

Remoção de elementos

Ex: Remover elemento com valor 3

2

p ro x p ro x

3 4

1

p ro x

ant

Lista* ref

while (p != NULL && p->info != elemBuscado) {

ant = p;

p = p->prox;

}

p

ant = NULL

(29)

Lista encadeada

Remoção de elementos

Ex: Remover elemento com valor 3

2

p ro x p ro x

3 4

1

p ro x

p ant

Lista* ref

while (p != NULL && p->info != elemBuscado) {

ant = p;

p = p->prox;

}

p

(30)

Lista encadeada

Remoção de elementos

Ex: Remover elemento com valor 3

2

p ro x p ro x

3 4

1

p ro x

p ant

Lista* ref

while (p != NULL && p->info != elemBuscado) {

ant = p;

p = p->prox;

}

ant

(31)

Lista encadeada

Remoção de elementos

Ex: Remover elemento com valor 3

2

p ro x p ro x

3 4

1

p ro x

p

Lista* ref

while (p != NULL && p->info != elemBuscado) {

ant = p;

p = p->prox;

}

ant p

(32)

Lista encadeada

Remoção de elementos

Ex: Remover elemento com valor 3

2

p ro x p ro x

3 4

1

p ro x

Lista* ref

ant p

if (p == NULL) //elemento não encontrado return ref;

//remove primeiro elemento if (p == ref)

ref = p->prox;

else //remove do meio ou fim da lista ant->prox = p->prox;

(33)

Lista encadeada

Remoção de elementos

Ex: Remover elemento com valor 3

2

p ro x p ro x

3 4

1

p ro x

Lista* ref

ant p

free(p);

return ref;

}

(34)

Lista encadeada

Remoção de elementos

Caso específico: remoção do primeiro elemento da lista

info 1

p ro x p ro x

info 2

info 3

Lista* ref

p

if (p == ref) ref = p->prox;

(35)

Lista encadeada

Remoção de elementos

Caso específico: remoção do primeiro elemento da lista

info 1

p ro x p ro x

info 2

info 3

Lista* ref

p

if (p == ref) ref = p->prox;

(36)

Lista encadeada

Remoção de elementos

Caso específico: remoção do primeiro elemento da lista

info 1

p ro x p ro x

info 2

info 3

Lista* ref

p

free(p);

return ref;

(37)

Lista encadeada

Liberando a memória da Lista

Caso específico: remoção do primeiro elemento da lista

Passagem de ponteiro para ponteiro

Alternativa para modificar ref sem ter que retornar nada

void libera(Lista** lista) {

Lista* l = *lista;

while (l != NULL) {

Lista* aux = l->prox;

free(l);

l = aux;

}

*lista = NULL;

}

(38)

Lista encadeada

int main() {

Lista* ref = criaLista();

ref = insere(ref, 1);

ref = insere(ref, 2);

ref = insere(ref, 3);

ref = insere(ref, 4);

imprimir(ref);

ref = remove(ref, 2);

imprimir(ref);

ref = remove(ref, 4);

imprimir(ref);

ref = remove(ref, 4);

imprimir(ref);

4 3 2 1 4 3 1

3 1

3 1

(39)

Lista encadeada

ref = remove(ref, 1);

imprimir(ref);

ref = remove(ref, 3);

imprimir(ref);

ref = insere(ref, 5);

ref = insere(ref, 6);

imprimir(ref);

libera(&ref);

imprimir(ref);

ref = insere(ref, 7);

ref = insere(ref, 8);

imprimir(ref);

printf("\n\nFim!");

getchar();

return 0;

}

3

Lista vazia!

6 5

8 7

Lista

vazia!

(40)

Lista encadeada circular

Considere uma lista simples, e um

ponteiro ‘p’ que aponta para algum dos elementos da lista

Problema: a partir de ‘p’, não podemos acessar os elementos anteriores a ‘p’

Solução: Último nó aponta para o primeiro

Definição de lista circular!

info 1

p ro x p ro x

info 2

info 3

p ro x

(41)

Lista encadeada circular

Não existe uma definição exata do primeiro e último elementos da lista

No entanto, pode ser adotada uma convenção

Referência para lista aponta para o último elemento

Assim, pode-se facilmente realizar inserções no início ou no fim da lista

info 1

p ro x p ro x

info 2

info 3

Lista* ref

p ro x

último nó primeiro

(42)

Lista encadeada circular

Para percorrer uma lista não-circular, testamos se o elemento acessado é NULL

for (aux = ref; aux != NULL; aux = aux->prox)

Em uma lista circular, deve ser mantida a referência para o último nó da lista

A cada elemento acessado, deve ser verificado se o fim da lista foi alcançado

Válido para busca, impressão de elementos, etc...

for (aux = ref->prox; aux != ref; aux = aux->prox)

Após o laço, último nó deve ser processado!

 

(43)

Lista encadeada circular

Inserção em uma lista vazia

Ponteiro ‘próximo’ deve apontar para o próprio elemento

info

Lista* ref

p ro x

if (ref == NULL) {

}

novoNo->prox = novoNo;

ref = novoNo;

novoN o

(44)

Lista encadeada circular

Inserção em uma lista contendo pelo menos um elemento

Insere no início

info

p ro x

if (ref == NULL)

{

novoNo->prox = novoNo;

ref = novoNo;

info

}

p ro x

novoN o

Lista* ref

else {

}

novoNo->prox = ref->prox;

(45)

Lista encadeada circular

Inserção em uma lista contendo pelo menos um elemento

Insere no início

info

p ro x

if (ref == NULL)

{

novoNo->prox = novoNo;

ref = novoNo;

info

}

p ro x

novoN o

Lista* ref

else {

}

novoNo->prox = ref->prox;

ref->prox = novoNo;

(46)

Lista encadeada circular

Inserção em uma lista circular

Lista* insereCircular(Lista* ref, int info) {

Lista* novoNo = (Lista*)malloc(sizeof(Lista));

novoNo->info = info;

if (ref == NULL) {

novoNo->prox = novoNo;

ref = novoNo;

} else {

novoNo->prox = ref->prox;

ref->prox = novoNo;

}

return ref;

}

(47)

Lista encadeada circular

Remoção de elementos

Ponteiro ‘p’ percorre a lista até que o

elemento a ser removido seja encontrado

Ponteiro auxiliar ‘ant’ também percorre a lista, estando sempre em uma posição anterior a ‘p’

Uma vez que o elemento é encontrado, o ponteiro de ‘ant’ aponta para o elemento apontado pelo ponteiro de ‘p’

...e ‘p’ é removido e sua memória liberada

(48)

Lista encadeada circular

Remoção de elementos

Ocorre de modo similar à remoção em uma lista simples

Porém, para encerrar busca elemento de referência é utilizado

No caso de listas simples, sabemos que o

elemento que aponta para NULL é o último da

lista

(49)

Lista encadeada circular

Remoção de elementos

Alguns tratamentos específicos

Remoção do único elemento de uma lista deve retornar NULL

Referencia externa para a lista recebe valor NULL, indicando que a lista se encontra vazia

if (p->prox == p) {

free(p);

return NULL;

}

info Lista* ref

p ro x

(50)

Lista encadeada circular

Remoção de elementos

Alguns tratamentos específicos

Caso o elemento a ser removido seja a referencia da lista

Referência deve ser atualizada

Nova referência deve ser o elemento anterior

if (p == ref) ref = ant;

(51)

Lista encadeada circular

Remoção de elementos

Lista* removeCircular(Lista* ref, int elemBuscado) {

Lista* p;

Lista* ant;

if (ref == NULL) return ref;

p = ref->prox;

ant = ref;

while (p->info != elemBuscado && p != ref) {

ant = p;

p = p->prox;

}

(52)

Lista encadeada circular

Remoção de elementos

if (p->info == elemBuscado) {

if (p->prox == p) {

free(p);

return NULL;

}

if (p == ref) ref = ant;

ant->prox = p->prox;

free(p);

p = NULL;

}

return ref;

}

(53)

Lista encadeada circular

Impressão de elementos

void imprimeListaCircular(Lista* ref) {

Lista* aux;

printf("\nimprimindo lista:\n");

if (ref == NULL) {

printf("\nLista vazia!\n");

return;

}

for (aux = ref->prox; aux != ref; aux = aux-

>prox)

printf("%d ", aux->info);

printf("%d ", aux->info);

}

(54)

Mais Variações

Lista duplamente encadeada

Lista circular duplamente encadeada

info righ t

left info righ

t

left info righ

t left

info righ t

left info righ

t

left info righ

t left

(55)

Vantagens em relação ao uso de vetores

Maior flexibilidade

Conjunto de dados pode crescer ou diminuir

Evita o disperdício de memória

Elementos podem ser removidos em posições específicas

E também inseridos em posições específicas

Mostre nos exercícios!

(56)

Desvantagens em relação ao uso de vetores

Acesso sequencial

Para alcançar o n-ésimo elemento, os ‘n-1’

elementos anteriores a ‘n’ devem ser acessados

Com vetores

Acesso constante

meuVetor[n]

Acesso mais rápido

(57)

Exercícios

1.

Crie sua própria implementação de Lista Encadeada, com as funcionalidades

apresentadas nesta aula

2.

Modifique a lista da questão 1 para

armazenar inteiros na ordem crescente

Elementos podem ser inseridos no meio ou no final da lista!

Modifique a operação ‘remover’ para que seja mais eficiente do que na lista da

questão 1 (somente a parte necessária da

lista deve ser processada)

(58)

Referências

Documentos relacionados

SÉRIE 85 Relé temporizador plug-in 7 - 10 A Eletromédica, odontologia Forno de secagem Elevadores Painéis para distribuição de energia Painéis de controle Temporizadores,

No final de 1980, com as descobertas da ação analgésica de opioides na medula espinhal, a analgesia epidural se tornou um mecanismo que trouxe o uso da técnica de

Importa ainda referir que, a prova desta disciplina contempla a componente escrita e prática, o grau de exigência resulta dos critérios gerais de classificação, balizados

Dando prosseguimento, o presidente em exercício, colocou em votação a proposta de menção de elogio para os membros da comissão eleitoral central e comissões

Os resultados apontam para o sentido de que a escola mesmo não sendo da sua cultura, é admitida pela necessidade de conviver com os não indígenas, partindo do

Inicialmente foi realizada uma visita à escola para obtenção de informações sobre os critérios de autorização para prosseguir com a pesquisa. Assim feito, foram

Quanto à comicidade de palavras, Brito (1999) é enfático ao dizer que Camões, através, mormente, do processo de repetição, cria inúmeras comicidades de palavras quando

O presente trabalho busca apresentar a pesquisa intitulada “Espaço de reflexão crítica em contexto de colaboração: reconstruindo os sentidos e os significados da prática educativa