Lista Duplamente Encadeada
Last updated
Was this helpful?
Last updated
Was this helpful?
As listas duplamente encadeadas são uma extensão da lista encadeada ligada apresentada no item anterior.
Uma lista duplamente encadeada é formada por um conjunto de nós composto normalmente por três elementos: uma variável que armazena a informação (carga
), podendo ser objetos, números, caracteres etc., e dois ponteiros que possibilitam a ligação entre os nós anterior (anterior
) e posterior (próximo
) desta lista.
Assim, enquanto que em uma lista encadeada simples, cada nó conhece apenas o próximo nó, nas listas duplamente encadeadas, os nós conhecem também o seu antecessor, com exceção da cabeça, que aponta apenas para o seu sucessor e da cauda, que aponta apenas para o seu antecessor.
Este tipo de solução permite percorrer uma lista nas duas direções. Por exemplo, do início (CABEÇA) até o final da lista (CAUDA),utilizaremos o ponteiro PRÓXIMO. Para percorrer a lista do final até o início (i.e., ordem inversa), devemos começar pelo último nó (CAUDA) e, através do ponteiro ANTERIOR, percorrer a lista até encontrar o primeiro nó (CABEÇA).
A figura a seguir mostra a representação gráfica de uma lista duplamente encadeada. Note que agora há a presença do ponteiro ant
(destacado em verde), que referencia o elemento anterior, o que possibilita a navegação nos dois sentidos.
Da mesma forma que a lista encadeada simples, vamos precisar utilizar o objeto No
para descrever cada elemento, e um objeto ListaEncadeada
que irá conter a referência para a cabeça e a cauda. A diferença é que agora o objeto No
precisará conter um atributo a mais que irá referenciar o elemento anterior, que chamaremos de ant
.
Vamos agora ver como funcionam as operações de inserção e remoção na lista duplamente encadeada considerando a seguinte lista:
Para inserir no início da lista duplamente encadeada, devem ser realizados os seguintes passos:
Atualizar o ponteiro prox
do novo nó a ser inserido para apontar para a cabeça atual
Mudar o apontador cabeca
da lista para apontar para o nó novo
Mudar o apontador ant
da antiga cabeça (agora segundo nó da lista) para apontar para a nova cabeça
Para inserir no final, seguimos as seguintes etapas:
Atribuir o apontador do anterior (ant
) do novo elemento para a cauda atual
Atualizar o elemento prox
da cauda atual para referenciar o novo nó
Atualizar a cauda da lista para apontar para o novo nó
Para remover um elemento do início da lista, devemos seguir as seguintes etapas:
Atualizar a cabeça da lista para ser o seu próximo elemento
Mudar o apontador anterior da cabeça atual (ant
) para apontar para None
, visto que agora ele tornou-se o primeiro nó da lista.
Note que agora o elemento que era a cabeça anteriormente não é apontado mais por nenhum elemento, logo ele será descartado pelo coletor de lixo.
Para remover um elemento do final, diferentemente do que era necessário na lista encadeada simples, não precisaremos mais percorrer a lista até o final. Como agora temos o apontador pro elemento anterior (ant
), podemos acessar diretamente a cauda e mudar o seu anterior para remover a referência para o próximo nó. Desta forma, o desempenho desta operação deixa de ser O(n) (tempo linear) para ser O(1) (tempo constante).
Para tal, devemos as seguintes etapas:
Mudar o apontador da cauda da lista para apontar para o seu anterior (penúltimo elemento)
Mudar o apontador prox
da nova cauda para apontar para None
Agora que temos o ponteiro que aponta para o nó anterior (ant
), torna-se bem mais fácil percorrer a lista no sentido inverso (do último até o primeiro). Para tal, utilizamos a mesma lógica do método imprimir da lista encadeada: criar uma variável temporária atual
que irá receber cada elemento da lista dentro de um while
. Porém, ao invés de atribuir inicialmente a variável atual
ao elemento cabeça e ir atribuindo o valor de atual ao seu próximo a cada iteração, iremos inicializar a variável atual recebendo a cauda e a cada iteração atribuir ela ao seu anterior.
A lista circular é uma variação da lista duplamente encadeada, em o ponteiro prox
do último elemento aponta para o primeiro elemento da lista (cabeça) e o ponteiro ant
do primeiro elemento aponta para o último elemento da lista (cauda), formando um círculo.
Uma atenção especial deve ser dada ao percorrer esse tipo de lista. Caso seja utilizada a mesma lógica da lista encadeada simples (percorrer até achar um nó que seja None
), haverá uma execução infinita desse método, já que não há mais apontadores None
em nenhuma parte da lista. Para resolver esse problema, a lista deve ser percorrida até encontrar a sua cauda. Exemplo:
Já o funcionamento dos métodos de inserção e remoção seguem a mesma lógica da lista encadeada não-circular. Apenas deve ser necessário trocar as referências de None
para apontar para a cabeça e ou a cauda de acordo com a situação. Exemplos: