Listas Lineares


Uma lista linear permite representar um conjunto de dados do mesmo tipo onde a ordem linear entre eles é preservada.

Alguns exemplos de utilização são:

As principais operações que podem ser executadas em Listas Lineares são:

 

Lista Seqüencial


Definição: Uma lista é dita seqüencial quando seus componentes são armazenados em posições contíguas na memória.

A implementação mais comum de uma lista seqüencial usa um vetor (array), mas pode ser também usado um arquivo ou conjunto.

Para ver uma implementação de uma lista Sequencial, abra a unit LstSeq e para um programa que demonstra como usá-la, veja DLstSeq.

 

Lista Encadeada


Definição: Uma lista encadeada é uma coleção de registros em que cada registro tem um campo que indica a localização do registro seguinte na lista.

Assim, a ordenação é fornecida explicitamente por esse campo. Esquematicamente podemos representar uma lista ligada da seguinte forma:

Onde o registro que representa cada componente da lista possui um campo que aponta para o próximo registro numa certa seqüência de ordenação.

Cabeça: Primeiro registro (elemento) da lista.

Cauda: Último elemento da lista. Para indicar que não ha elementos após a cauda, atribui-se nil ao apontador para o próximo, já que não existe um próximo.

A principal vantagem das listas encadeadas sobre as listas seqüenciais é que as primeiras podem aumentar e reduzir de tamanho dinamicamente, podendo assim ser usadas em diversas situações sem comprometer o uso de memória.

A principal desvantagem das listas encadeadas é a necessidade de armazenamento adicional para guardar os apontadores.

Para ver uma implementação de uma lista Encadeada, abra a unit LstEnc e para um programa que demonstra como usá-la, veja DLstEnc.

 

Inserção na lista encadeada


Inicialmente a cabeça da lista aponta para nil, indicando que a mesma está vazia (1). Após a inserção do um Item 1 na lista, a cabeça aponta para ele, e o apontador para o próximo Item contido no Item 1 aponta para nil, já que após ele não há outros itens (2). Caso um outro elemento (Item 2) seja inserido, ele passa a ser a cabeça da lista e seu apontador para o próximo elemento da lista aponta para a antiga cabeça da lista (Item 1), como pode ser verificado em (3).

 

Remoção na lista encadeada


Quando o item a ser removido encontra-se na cabeça da lista, executamos os seguintes passos:

1) Navegamos na lista até encontrar o item a ser removido e guardamos o apontador para o próximo item (segundo) da lista.

2) Fazemos com que o apontador para a cabeça passe a apontar para o item seguinte à cabeça da lista, através do apontador obtido no passo 1.

3) Já que a cabeça já foi ajustada, executamos o dispose no item que localizava-se na cabeça da lista, liberando a memória referente ao Item removido da lista.

4) A lista passa a ter um elemento a menos e a variável dinâmica que continha o elemento apontado pela cabeça da lista foi desalocado da memória.

Quando o item a ser removido não se encontra na cabeça da lista, é necessário atualizar o apontador do item anterior. Sendo assim, usamos um apontador (PAtual) para procurar o Item a ser removido e outro (PAnterior) para guardar o Item anterior na lista encadeada. Desta forma, ao encontrar o Item (1), temos um apontador para o item anterior a ele (PAnterior) e para o próprio item (PAtual), como mostra (2). De posse destes apontadores, podemos ajustar o apontador para o próximo do item apontador por PAnterior para o endereço apontado pelo campo próximo do item apontado por PAtual. Em seguida, usamos o dispose para desalocar a variável dinâmica que guarda o item removido (3). Ao final, temos uma lista encadeada com um item a menos e com todos os apontadores ajustados.

 

Lista Ordenada


Definição: É uma lista que mantém os seus elementos sempre ordenados por algum critério.

Deve ser usada quando é necessário acessar os elementos da lista de forma ordenada. Quando a ordem não for importante, deve-se considerar o uso de outro tipo de lista pois as operações de inserção e remoção são mais caras (exigem mais processamento)  nas listas ordenadas.

A forma mais comum de se implementar uma lista ordenada é usando uma lista encadeada, onde o primeiro nó é menor que o segundo e assim por diante. Graficamente teríamos:

Para

Para ver uma implementação de uma lista Ordenada, abra a unit LstOrd e para um programa que demonstra como usá-la, veja DLstOrd.

O procedimento inicializar faz com que a cabeça da lista aponte para nil, indicando que não há elementos na lista.

O procedimento apagar e as funções cheia, vazia e tamanho são iguais à da lista encadeada, recebendo apenas um parâmetro com tipo diferente.

A função inserir coloca o item passado na posição correta da lista, ou seja, mantendo a ordem da mesma. A lista não deve conter uma chave igual à do item passado. Se a chave do item passado for menor do que a do primeiro da lista, será inserido no início e tornar-se-á a cabeça da lista (situação 1). Caso contrário, será inserido no meio (situação 3) ou no final (situação 2) da lista, dependendo do valor de sua chave e dos elementos contidos na lista (situação 2).

Situação 1: Inserindo um valor no início da lista:

 

Situação 2: Inserindo um valor no final da lista:

 

Situação 3: Inserindo um valor no meio da lista:

 

A função remover encontra um item na lista com a chave passada, libera a memória alocada pelo item e ajusta apontadores. No caso da remoção, encontramos 2 situações: remoção do elemento da cabeça ou que não está na cabeça.

Situação 1: Removendo um item da cabeça da lista:

 

Situação 2: Removendo um item que não está na cabeça da lista:

 

Lista Circular


Definição: É uma lista encadeada na qual o último elemento aponta para a cabeça da lista.

Graficamente teríamos:

As listas vistas até agora não possuem nó cabeça (apesar de poderem). Um nó cabeça é um nó criado na inicialização da lista e que permanece sendo a cabeça mesmo após inserções e remoções. O nó cabeça não guarda informações, a não ser o apontador para o primeiro nó da lista.

Uma lista circular com nó cabeça vazia teria a seguinte estrutura:

 

Já uma lista circular com 2 itens teria a seguinte estrutura:

Observe que em ambas as situações o nó cabeça não é usado para armazenar valores.

Para ver uma implementação de uma lista Circular com no cabeça, abra a unit LstCirC e para um programa que demonstra como usá-la, veja DLstCirC.

O procedimento Inicializar faz com que o nó cabeça seja criado e ajusta o tamanho para zero. Também faz com que o apontador para o próximo contido no nó cabeça aponte para ele mesmo.

A função Inserir cria um novo nó e o coloca após o nó cabeça, fazendo com que o seu próximo seja igual ao anteriormente apontado pelo nó cabeça.

 

A função Remover retira o primeiro nó com a chave informada. Se encontrar, ajusta os apontadores e apaga o nó.

Observe que mesmo quando o último elemento é removido, o nó cabeça continua, conforme mostrado abaixo:

 

O procedimento para Apagar libera todos os nós da lista, incluindo o nó cabeça. Não é correto chamar nenhuma outra operação (a não ser Inicializar) após chamar Apagar.

 

Lista Duplamente Encadeada


Definição: Uma lista duplamente encadeada (ou duplamente ligada) é uma lista com ligações múltiplas em que cada nó tem um campo que aponta para o nó anterior na lista e outro campo que aponta para o nó seguinte.

Graficamente temos:

 

As listas duplamente encadeadas são bastante úteis quando desejamos percorrer os nós em qualquer sentido, como em um editor de textos (cada linha é um nó) ou uma agenda (cada pessoa é um nó).

Uma lista duplamente encadeada possui um apontador para o início da lista (cabeça) e outro para o final (cauda).

Para ver uma implementação de uma lista Circular com no cabeça, abra a unit LstDEnc e para um programa que demonstra como usá-la, veja DLstDEnc.

A função Inserir coloca o novo nó na cabeça da lista. Se a lista estiver vazia, o novo nó também será a cauda.

 

A função Remover apaga um nó da lista e ajusta os apontadores (cabeça, cauda, anterior e proximo) quando necessário

a) Removendo no meio

 

b) Removendo na cabeça

 

c) Removendo na cauda

 

d) Removendo o último

 

As funções Tamanho, Cheia e Vazia são semelhantes às da lista circular, diferindo apenas nos tipos de parâmetros (passam de Lista_Circular para Lista_Duplamente_Enc).