Listas Ligadas Estruturas de Dados e Algoritmos 05/11/2015 Fábio Lopes Caversan 1 Listas Ligadas Desvantagens do armazenamento seqüencial para representar pilhas e filas: Subdimensionamento ou superdimensionamento da memória devido a alocação fixa de uma certa quantidade de memória antes da execução do programa Possibilidade de overflow 05/11/2015 Fábio Lopes Caversan 2 Compartilhando a memória disponível Suponha uma implementação seqüencial de pilhas Stack x,y,z; Suponha que sejam inicializadas, de forma estática, com 50 elementos x,y,z terão, durante a utilização, respectivamente, no máximo, 20, 10 e 70 elementos 05/11/2015 Fábio Lopes Caversan 3 Fazendo cálculos 100 células seriam necessárias Porém, na inicialização, deve-se usar o tamanho 70 para não ocorrer um overflow Então, temos 3* 70 = 210,quando somente 100 serão utilizadas Que tal evitar esse desperdício através de um gerenciador de memória? Ele controlaria quem recebe mais ou menos células de memória 05/11/2015 Fábio Lopes Caversan 4 Agrupando as células livres num banco de memória livre Banco de memória 05/11/2015 Variáveis x y z nada nada nada Fábio Lopes Caversan 5 Operação Banco de memória x.Push(a) x y z (nada) (nada) y.Push(b) (nada) y.Push(c) (nada) x.Push(d) (nada) z.Push(e) 05/11/2015 (nada) Fábio Lopes Caversan 6 Operação Banco de memória z.Push(f) (nada) x y z y.Pop() z.Pop() (nada) x.Pop() (nada) z.Push(g) 05/11/2015 Fábio Lopes Caversan 7 Considerações importantes A ordem das células de memória disponíveis no banco, no decorrer da execução da aplicação, muda substancialmente O importante é manter um controle sobre a ordem lógica das células de memória e não, necessariamente, sobre a ordem física 05/11/2015 Fábio Lopes Caversan 8 Lista Ligada (Encadeada) Linear Cada item na lista é um nó Todo nó contem dois campos: o de informação e do endereço seguinte Campo de informação: armazena o real elemento da lista Campo de endereço: endereço do próximo nó na lista. É um ponteiro! 05/11/2015 Fábio Lopes Caversan 9 Algumas representações do nó ... Endereço do próximo nó Informação nulo 05/11/2015 Fábio Lopes Caversan 10 Continuando . . . A lista ligada inteira é acessada a partir de um ponteiro externo que aponta para o primeiro nó da lista Um ponteiro externo não está incluindo dentro de um nó. É acessado por referência a uma variável O campo do próximo endereço do último nó da lista contém um valor especial: null O ponteiro nulo (null) marca o final da lista Uma lista sem nós é uma lista vazia ou nula Nesse caso, o ponteiro externo para a lista é nulo Uma lista pode ser inicializada por uma operação do tipo list = null (sendo list o ponteiro externo) 05/11/2015 Fábio Lopes Caversan 11 Revisando o mapa conceitual de um programa na memória Pilha Heap Endereços de retorno de funções, variáveis locais Alocação dinâmica: listas encadeadas Variáveis Globais Código do Programa 05/11/2015 Fábio Lopes Caversan 12 Ao “fotografar” a memória do computador Programa Área Livre (Heap) LIST Ponteiro externo que aponta para o primeiro nó da lista ligada. Cuidado com sua manipulação! 05/11/2015 Fábio Lopes Caversan 13 L N1 N2 N3 . . . Nn null Dois endereços merecem atenção especial: • O endereço L do nó inicial que permite identificar em que parte da memória inicia-se a lista • O endereço NULL que vem após o nó final. É onde termina a lista Sempre lembrando, cada nó i é composto de duas partes: • Info: área onde é armazenado o i-ésimo elemento da lista • Next: área onde é armazenado o “endereço” do nó N i + 1 05/11/2015 Fábio Lopes Caversan 14 Declarando uma classe Nó para uma lista ligada Um dos atributos da classe precisa ser um ponteiro para uma estrutura do mesmo tipo public class Node { private object info; private Node next; ... }; 05/11/2015 Fábio Lopes Caversan 15 Permitindo o acesso aos atributos De acordo com a linguagem, pode ser necessária a escrita de métodos para ler e escrever nos atributos, garantindo o encapsulamento. Nas linguagens que possuem propriedades, isso pode ser feito com os comandos get e set. Cada vez que atribuímos um valor para a propriedade, o set é chamado. Cada vez que lemos um valor da propriedade, o get é chamado. 05/11/2015 Fábio Lopes Caversan 16 public class Node { private object info; private Node next; public object Info { get {return info;} set {info = value;} } public Node Next { get {return next;} set {next = value;} } ... }; 05/11/2015 Fábio Lopes Caversan 17 Incluindo um elemento no início da lista info list next info next 5 3 info next 8 nulo p info list p 5 05/11/2015 info next info next 3 8 nulo info next info next 3 8 6 info list next 5 next Fábio Lopes Caversan nulo 18 6 p 5 3 8 nulo list p list 6 5 3 8 nulo list 6 5 3 8 nulo 05/11/2015 Fábio Lopes Caversan 19 Os passos anteriores são expressos pelo seguinte algoritmo Node p = new Node(); p.Info = x; p.Next = list; list = p; 05/11/2015 Fábio Lopes Caversan 20 Removendo um nó do início de uma lista info 7 list P list 7 X=7 P 7 next info next info next 5 9 nulo 5 9 nulo 5 9 nulo list P 7 5 9 nulo 5 9 nulo 5 9 nulo list P 7 list list 05/11/2015 Fábio Lopes Caversan 21 Os passos anteriores para remover um nó são expressos pelo seguinte algoritmo Node p = list; list = p.Next; x = p.Info; 05/11/2015 Fábio Lopes Caversan 22 Implementação ligada de Pilhas Incluir um elemento no início de uma lista ligada é semelhante à inclusão numa pilha O 1º nó da lista representa o topo da pilha Vantagem: todas as pilhas ou filas usadas por um programa podem compartilhar a mesma lista de nós disponíveis, desde que não ultrapassam a quantidade total de nós disponíveis 05/11/2015 Fábio Lopes Caversan 23 Uma pilha usando lista ligada stack 5 3 8 7 nulo 5 3 8 7 nulo stack 6 05/11/2015 Fábio Lopes Caversan 24 Como o 1º nó da lista é o topo da lista A implementação de Push (x) poderia ficar: Node p = new Node( ); p.Info = x; p.Next = stack; stack = p; 05/11/2015 Fábio Lopes Caversan 25 A implementação de x = pop (s) poderia ficar : if (Empty ()) // Exception, mensagem, etc... else { Node p = stack; stack = p.Next; x = p.Info; p = null; return x; } 05/11/2015 Fábio Lopes Caversan 26 Uma fila usando lista ligada final início 4 1 7 9 3 final início 4 1 05/11/2015 nulo 7 9 Fábio Lopes Caversan 3 6 nulo 27 Algoritmo para x = q.Remove () if (Empty()) // Exceção, mensagem, etc... Node p = front; x = p.Info; front = p.Next; if (front = = null) rear = null; p = null; return ( x ); 05/11/2015 A fila q consiste numa fila e dois ponteiros: front e rear. As operações q.Empty() e x=q.Remove() são análogas a s.Empty(s) e x=s.Pop(s), basta substituir front por stack. Muita atenção ao remover o último elemento da fila: rear fica também nulo porque se a fila está vazia front e rear devem ser nulos Fábio Lopes Caversan 28 Algoritmo para q.Insert (x) Node p = new Node(); p.Info = x; p.Next = null; if (rear = = null ) front = p; else rear.Next = p; rear = p; 05/11/2015 Fábio Lopes Caversan 29 Desvantagens de pilhas e filas como listas ligadas? Mais armazenamento do que o vetor: Info e Next Mas o espaço de armazenamento não é o dobro. Depois, pode-se compactar as informações Cada inclusão/exclusão corresponde a uma inclusão/exclusão na lista de nós disponíveis Grande vantagem: compartilhamento de nós, desde que não ultrapasse o número de nós disponíveis 05/11/2015 Fábio Lopes Caversan 30 Listas ligadas como estruturas de dados São importantes não só para implementar pilhas e filas, mas como estruturas de dados Acessa-se um item percorrendo-se a lista a partir do início Um vetor permite o acesso direto ao enésimo item com uma única operação Já uma lista exige n operações e é necessário percorrer cada um dos primeiros n-1 elementos antes do enésimo Vantagem da lista: aparece na inserção ou remoção de um elemento porque não é necessário nenhuma movimentação dos n elementos 05/11/2015 Fábio Lopes Caversan 31 Inserção de um novo elemento num vetor x0 x1 x2 x3 x4 x5 05/11/2015 x0 x1 x2 x3 x4 x5 Fábio Lopes Caversan x0 x1 x2 x x3 x4 x5 x6 32 Já numa lista o trabalho de inserção independe do seu tamanho n list X0 X1 X2 X3 X4 nulo X1 X2 X3 X4 nulo n list X0 InsertAfter (n,x) X DeleteAfter(n) Node p = new Node() Node p = n.Next; p.Info = x; x = p.Info; p.Next = n.Next; n.Next = p.Next; n.Next = p p = null; 05/11/2015 Fábio Lopes Caversan 33