Estruturas de Dados I Rodrigo Porfírio da Silva Sacchi rodrigosacchi@ufgd.edu.br 3410-2075 Aula 6: Listas Encadeadas e Alocação Dinâmica
Listas Encadeadas e Alocação Dinâmica
Alocação Seqüencial versus Alocação Dinâmica Seqüencial ou Estática: Operações em listas implementadas com alocação seqüencial são muito fracas; Na utilização de mais de duas listas, a gerência de memória é mais complexa usar alocação dinâmica (ou alocação encadeada); Na alocação encadeada, as posições de memória são alocadas (ou desalocadas) na medida em que são necessárias (ou dispensadas).
Alocação Seqüencial versus Alocação Dinâmica Seqüencial ou Estática: Todo espaço de memória a ser utilizado (para armazenar os elementos) é reservado (alocado) em tempo de compilação; Esse espaço de memória permanece alocado durante toda execução do programa, independente de estar efetivamente sendo usado ou não.
Alocação Seqüencial versus Alocação Dinâmica Encadeada ou Dinâmica: O espaço de memória a ser utilizado (para armazenar os elementos) pode ser alocado em tempo de execução, quando for efetivamente necessário (é possível alocar espaço para um elemento de cada vez); O espaço alocado pode ser liberado em tempo de execução, quando não for mais necessário.
Alocação Seqüencial versus Alocação Dinâmica Vantagens e Desvantagens: A alocação encadeada é mais conveniente quando o problema inclui o tratamento de mais de uma lista; O acesso ao k-ésimo elemento da lista é imediato na alocação seqüencial, mas na alocação encadeada obriga ao percurso na lista até o elemento desejado.
Alocação Seqüencial versus Alocação Dinâmica Seqüencial ou Estática: 5 C B A 4 3 2 1 Topo
Alocação Seqüencial versus Alocação Dinâmica Encadeada ou Dinâmica: B 5 Depois de Depois de mim vem o... A 4 3 mim vem o... C 2 Topo 1
Listas Encadeadas Uma lista encadeada L, com n blocos de memória B 1, B 2,... B n é definida pelas seguintes características: Cada bloco de memória B i, 0 <= i < n, ou cada nó da lista tem pelo menos dois campos: Informação a ser armazenada; Indicação do próximo elemento na lista. Os blocos não estão necessariamente em seqüência na lista.
Listas Encadeadas Continuação (...) O acesso aos elementos da lista ocorre através de um ponteiro do início da lista (primeiro elemento); O acesso aos demais elementos ocorre através da indicação de quem é o próximo elemento na seqüência; O último nó da lista indica um endereço inválido, chamado NIL ou NULL.
Listas Encadeadas Representação: Ptr (ponteiro para início da lista L) A B C D B 1 B 2 B 3 B n Fim da lista NULL Se Ptr apontar para NULL (fim da lista), significa que a lista está vazia.
Listas Encadeadas Nas listas encadeadas os nós não estão, obrigatoriamente, em posições contíguas da memória; As operações de inserção ou remoção de elementos requerem encontrar novas posições de memória para armazenamento e liberar outras que possam ser reutilizadas posteriormente; É preciso, portanto, gerenciar a memória disponível;
Listas Encadeadas Linguagens de alto nível possuem um módulo de gerência de memória disponível ao usuário. Basta fazer referência às rotinas internas de ocupação e devolução de nós da lista de espaço disponível; Em C/C++, as rotinas new() e delete() executam essa tarefa. Outras: malloc(), free(); Em Java, a única rotina para acomodar a tarefa de alocação de memória é: new().
Listas Encadeadas Nos algoritmos, usamos uma notação especial: A indicação do endereço de um nó, feita por um ponteiro, será representada pelo símbolo. Cada ponteiro utilizado é associado a um único tipo de nó; Assim, pt info representa o campo info do nó apontado por pt; pt prox representa o endereço do campo prox apontado por pt.
Listas Encadeadas Considere uma lista L. ptr Chave Nó Registro Próximo Como declarar um nó: declare No registro (chave: inteiro, prox: ponteiro de No); declare ptr: ponteiro No;
Listas Encadeadas Tipo de listas: Listas simplesmente encadeadas; Listas circulares; Listas duplamente encadeadas. Estruturas de dados que podemos utilizar: Listas; Pilhas; Filas.
Listas Simplesmente Encadeadas Qualquer estrutura, inclusive listas, que seja armazenada em alocação encadeada requer o uso de um ponteiro que indique o endereço de seu primeiro nó; O percurso de uma lista é feito a partir desse ponteiro. A idéia consiste em seguir, consecutivamente, pelos endereços existentes no campo que indica o próximo nó (semelhante ao índice da alocação seqüencial).
Listas Simplesmente Encadeadas Qualquer estrutura que usa alocação encadeada requer o uso de um ponteiro que indique o endereço de seu primeiro nó (elemento); O percurso em uma lista é feito a partir desse ponteiro;
Listas Simplesmente Encadeadas A idéia consiste em seguir pelos endereços existentes no campo que indica o próximo nó (semelhante ao índice da alocação seqüencial); Exemplo: percorrer uma lista para imprimir o campo chave.
Listas Simplesmente Encadeadas Ptr 3 9 1 5
Listas Simplesmente Encadeadas Algoritmo para imprimir os valores dos campos chave de todos os elementos na lista: procedimento Imprimir(ptr: ponteiro No) início declare pont ptr: ponteiro No; enquanto pont!= null faça escreva pont chave; pont pont prox; fim_enquanto fim
Listas Simplesmente Encadeadas Busca: Buscar em lista ordenada e em lista não ordenada; Se a lista possui elementos, percorrer a lista até que o elemento seja encontrado. Caso contrário, ou a lista está vazia, ou já percorremos toda e o elemento não foi encontrado; Deve ser eficiente.
Listas Simplesmente Encadeadas O ponteiro pont retorna com valor nulo caso o elemento são seja encontrado, caso contrário, retorna com um valor do endereço do elemento na memória. Ptr 3 9 1 5
Listas Simplesmente Encadeadas Algoritmo de busca em listas encadeadas: função ponteiro No Busca(ptr: ponteiro No, x: inteiro) início declare pont: ponteiro No; pont ptr; enquanto( pont!= null e pont chave!= x ) faça pont pont prox; fim_enquanto se( pont!= null ) então retorne pont; fim_se retorne null; fim
Listas Simplesmente Encadeadas Inserção: Após a realização da busca, a operação de inserção em uma lista encadeada é trivial; Três operações devem ser cumpridas: Alocação de memória para o novo elemento (nó); Utilização do nó; Acerto da lista.
Listas Simplesmente Encadeadas Algoritmo de inserção: Inserção no início; Inserção no fim; Inserção em uma lista ordenada.
Listas Simplesmente Encadeadas Inserção no início: procedimento InsereInicio(ptr: ponteiro No, int novo) início declare pont Busca(ptr, novo): ponteiro No; se( pont!= null) então escreva Elemento já existe. ; senão Alocar( pont ); pont chave novo; pont prox ptr; ptr pont; fim_se fim
Listas Simplesmente Encadeadas Queremos inserir o elemento novo = 8. Ptr 3 9 1 5
Listas Simplesmente Encadeadas Remoção: Como ocorre na inserção, após a realização da busca, a operação de remoção em uma lista encadeada é trivial; Três fases devem ser cumpridas: Utilização do nó (se for o caso); Acerto da lista; Desalocação de memória.
Listas Simplesmente Encadeadas Algoritmo de Remoção: Remoção no início; Remoção no fim; Remoção de qualquer elemento; Remoção em uma lista ordenada.
Listas Simplesmente Encadeadas Remoção no início: procedimento RemoveInicio(ptr: ponteiro No) início declare pont ptr; se( ptr!= null ) então ptr ptr prox; Desalocar( pont ); fim_se fim
Listas Simplesmente Encadeadas Queremos remover o elemento = 3. Ptr 8 3 9 1 5
Listas Circulares A busca em uma lista ordenada, vista anteriormente, pode ser considerada pouco eficiente quando comparada às outras buscas anteriormente mencionadas. Uma pequena modificação na estrutura física da lista pode ser de grande auxílio: Obrigar o último nó da lista a apontar para o nócabeça, criando uma lista circular encadeada.
Listas Circulares A B C D B 1 B 2 B 3 B n O último sempre deve apontar para o primeiro.
Listas Circulares Problema: o teste de fim de lista nunca é satisfeito; Solução: colocar a chave procurada no nó cabeça, de forma que uma resposta positiva seja sempre encontrada. Como ficam a busca, inserção e remoção? A busca é semelhante à lista simplesmente encadeada: o ponteiro pont tem o valor de null se o elemento não é encontrado.
Listas Duplamente Encadeadas A lista duplamente encadeada incorpora um novo campo de ponteiro; Os campos de ponteiros tomam os nomes de prev e prox (nó anterior e nó seguinte, respectivamente); Como fica a inicialização do nó cabeça? Os algoritmos de busca, inserção e remoção em tabelas ordenadas são muito simples; Na busca, a função retorna indicando o nó procurado ou, se este não foi encontrado, o nó que seria seu consecutivo.
Listas Duplamente Encadeadas A B C D B 1 B 2 B 3 B n
Listas Duplamente Encadeadas Busca em uma lista duplamente encadeada: função ponteiro No Busca(ptr: ponteiro No, x inteiro) início declare aux: ponteiro No; aux = ptr; enquanto( aux && aux chave!= x) faça aux = aux prox; fim_enquanto se ( aux!= null ) então retorne aux; fim_se retorne null; fim
Listas Duplamente Encadeadas Inserção no início da lista: procedimento InsereInicio(ptr: ponteiro No, x: inteiro) início declare novo: ponteiro No; Aloca(novo, x); se ( novo!= null ) então se( ptr == null ) então ptr = novo; senão novo prox = ptr; ptr prev = novo; ptr = novo; fim_se numelementos numelementos + 1;; fim_se fim
Listas Duplamente Encadeadas Remoção no início da lista: procedimento removeinicio(ptr: ponteiro No) Início declare aux: ponteiro No; se( ptr!= null ) aux = ptr; ptr = ptr prox; ptr prev = 0; Desaloca( aux ); numelementos numelementos 1; fim_se fim
Listas Duplamente Encadeadas Idéias de implementação: Para qualquer lista, seja ela simplesmente ou duplamente encadeada, podemos implementá-la sendo circular ou não; Além disso, podemos também implementar qualquer lista usando dois ponteiros; Além de um ponteiro que sempre aponta para o início da lista, podemos também controlar um ponteiro que sempre aponta para o final da lista; Isto é interessante tanto em listas simplesmente encadeadas quanto em listas duplamente encadeadas. Por que?