Lista duplamente vinculada: C++, Python (Exemplo de código)
O que é uma lista duplamente vinculada?
Em uma lista duplamente vinculada, cada nó possui links para o nó anterior e para o próximo. Cada nó consiste em três elementos: um contém os dados e outros dois são os ponteiros do nó seguinte e do nó anterior. Esses dois ponteiros nos ajudam a avançar ou retroceder a partir de um nó específico.
Aqui está a estrutura básica da lista duplamente vinculada.

Cada lista vinculada possui um nó inicial e um nó final. O nó principal não possui um nó “anterior” (ponteiro anterior) e o nó final não possui um nó “próximo”.
Aqui estão alguns termos importantes para uma lista duplamente vinculada:
- prev: Cada nó está vinculado ao seu nó anterior. É usado como um ponteiro ou link.
- Seguinte: Cada nó está vinculado ao seu próximo nó. É usado como um ponteiro ou link.
- Data: Isso é usado para armazenar dados em um nó. “Dados” podem conter outros Estruturas de dados dentro dele. Por exemplo, string, dicionário, conjunto, hashmap, etc. podem ser armazenados em “Dados”.
Aqui está a estrutura básica de um único nó na lista duplamente vinculada:
Operações de lista duplamente vinculada
As operações de uma lista duplamente vinculada incluem adicionar e excluir nós, inserir e remover nós e percorrer a lista vinculada de cima para baixo.
Aqui está a lista de operações que podemos implementar usando a lista duplamente vinculada:
- Inserção na frente
- Inserção na cauda ou último nó
- Inserção após um nó
- Inserção antes de um nó
- Exclusão da frente
- Exclusão da cauda
- Pesquise e exclua um nó
- Atravessar da cabeça à cauda
- Atravesse a cauda até a cabeça
Veremos a implementação e o pseudocódigo de todas as operações acima.
Inserção na frente da lista duplamente vinculada
A inserção na frente significa que precisamos criar um nó na lista vinculada e colocá-lo no início da lista vinculada.
Por exemplo, existe um determinado nó “15”. Precisamos adicionar isso ao nó principal.
Aqui estão duas condições importantes ao fazer esta operação:
- O novo nó será o nó principal se não houver nenhum nó na lista duplamente vinculada.
- Se já existir um nó principal, o nó anterior será substituído pelo novo nó.
Aqui está o pseudocódigo para esta operação:
function insertAtFront (ListHead, value): newNode = Node() newNode.value = value ListHead.prev = NewNode NewNode.next = ListHead newNode.prev = NULL return ListHead

Inserção no final da lista duplamente vinculada
“Inserção no final” significa que criaremos um nó na lista vinculada e o colocaremos no final.
Para fazer isso, podemos usar dois métodos:
- Método 1: Comece a percorrer a partir do início da lista duplamente vinculada até que o “próximo” se torne nulo. Em seguida, vincule o novo nó ao “próximo”.
- Método 2: Pegue o último nó da lista duplamente vinculada. Então, o “próximo” nó do último nó será vinculado ao novo nó. Agora, o novo nó será o nó final.
Aqui está o pseudocódigo para inserção no nó final:
function insertAtTail(ListHead, value): newNode = Node() newNode.value = value newNode.next = NULL while ListHead.next is not NULL: then ListHead = ListHead.next newNode.prev = ListHead ListHead.next = newNode return ListHead
Inserção após um nó
Digamos que temos uma lista duplamente vinculada como a seguinte:
Queremos inserir um determinado nó que será vinculado após o nó, que possui o valor “12”.
Aqui estão as etapas para isso:
Passo 1) Percorra da cabeça até o último nó. Verifique qual nó tem o valor “12”.
Passo 2) Crie um novo nó e atribua-o como o próximo ponteiro do nó “12”. O “próximo” nó do novo nó será 15.
Aqui está o pseudocódigo para inserir um nó após um nó em uma lista duplamente vinculada:
function insertAfter(ListHead, searchItem, value): List = ListHead NewNode = Node() NewNode.value = value while List.value is not equal searchItem then List = ListHead.next List = List.next NewNode.next = List.next NewNode.prev = List List.next = NewNode
Inserção antes de um nó
Esta operação é mais parecida com a “inserção após um nó”. Precisamos procurar o valor de um nó específico para fazer isso. Em seguida, criaremos um novo nó e o inseriremos antes do nó pesquisado.
Digamos que queremos inserir um determinado nó “15” antes do nó “12”. Então aqui estão as etapas para fazer isso:
Passo 1) Percorra a lista vinculada do nó principal ao nó final.
Passo 2) Verifique se o próximo ponteiro do nó atual tem o valor “12” ou não.
Passo 3) Insira o novo nó como o “próximo” nó do nó atual.
Aqui está o pseudocódigo para inserir um nó antes de um nó em uma lista duplamente vinculada:
function insertBefore(ListHead, searchItem, value): List = ListHead NewNode = Node() NewNode.value = value while List.next.value is not equal searchItem then List = ListHead.next NewNode.next = List.next NewNode.prev = List List.next = NewNode
Exclua o topo da lista duplamente vinculada
O nó principal na lista duplamente vinculada que não possui nenhum nó anterior. Portanto, o próximo ponteiro será o novo nó principal se quisermos excluir o nó principal. Também precisamos liberar a memória ocupada por um nó ao excluir um nó.
Aqui estão as etapas para excluir o nó principal:
Passo 1) Atribua uma variável ao nó principal atual.
Passo 2) Visite o nó “próximo” do nó principal atual e torne o nó “anterior” (ponteiro anterior) como ''NULL”. Isso significa que estamos desconectando o segundo nó do primeiro nó.
Passo 3) Libere a memória ocupada pelo nó principal anterior.
Aqui está o pseudocódigo para excluir o cabeçalho de uma lista duplamente vinculada:
function deleteHead(ListHead): PrevHead = ListHead ListHead = ListHead.next ListHead.prev = NULL PrevHead.next = NULL free memory(PrevHead) return ListHead
É necessário deletar a memória alocada após realizar qualquer tipo de operação de deleção. Caso contrário, durante todo o tempo de execução do programa, a memória do bloco excluído permanecerá ocupada. Nenhum outro aplicativo pode usar esse segmento de memória.
Exclua o final da lista duplamente vinculada.
Esta operação é semelhante à exclusão da cabeça. Aqui, em vez da cabeça, precisamos deletar a cauda. Para identificar um nó como nó final, devemos verificar se o próximo ponteiro ou o próximo nó é nulo ou não. Depois de deletar a cauda, precisamos liberar memória.
Esta operação também é conhecida como “exclusão por trás”.
Aqui estão as etapas para fazer isso:
Passo 1) Percorra até o nó final da lista duplamente vinculada.
Passo 2) Atribua uma variável ou ponteiro ao nó final.
Passo 3) Torne o “próximo” nó como NULL e libere a memória do nó final.
Aqui está o pseudocódigo para excluir o nó final:
function DeleteTail( ListHead ): head = ListHead while ListHead.next is not NULL: ListHead = ListHead.next Tail = ListHead.next ListHead.next = NULL free memory( Tail ) return head
Pesquise e exclua um nó da lista duplamente vinculada
Esta operação nos permite pesquisar dados de um nó específico e excluí-lo. Precisamos realizar uma pesquisa linear, pois a lista vinculada é uma estrutura de dados linear. Após a exclusão, também precisamos liberar memória.
Aqui estão as etapas para pesquisar e excluir um nó na lista duplamente vinculada:
Passo 1) Percorra a lista encadeada desde o início até que o valor do nó seja igual ao item de pesquisa.
Passo 2) Atribua uma variável “deleteNode” ao nó correspondente.
Passo 3) Atribua o nó anterior de “deleteNode” ao próximo nó.
Passo 4) Libere a memória do “deleteNode”
Aqui está o pseudocódigo para pesquisar e excluir um nó de uma lista vinculada:
function SearchAndDelete(ListHead, searchItem): head = ListHead while ListHead.next.value not equals searchItem: head = head.next deleteNode = head.next head.next = head.next.next head.next.prev = head deleteNode.next, deleteNode.next = NULL free memory(deleteNode) return ListHead
Percorrer uma lista duplamente vinculada de frente
Para percorrer a partir do nó principal e iterar no próximo nó até encontrar “NULL”. Ao percorrer cada nó, podemos imprimir o valor do nó. Aqui estão as etapas para atravessar pela frente (direção para frente):
Passo 1) Atribua um ponteiro ou variável ao nó principal atual.
Passo 2) Itere para o próximo nó do cabeçalho até obter “NULL”
Passo 3) Imprima os dados do nó em cada iteração.
Passo 4) Retorne o nó principal.
Aqui está o pseudocódigo para percorrer uma lista duplamente vinculada pela frente:
function traverseFromFront(ListHead): head = ListHead while head not equals to NULL: print head.data head = head.next return ListHead
Aqui, a devolução não é obrigatória. Mas é uma boa prática retornar o nó principal após as operações.
Percorra uma lista duplamente vinculada de trás para frente
Esta operação é o inverso da poligonal pela frente. A abordagem é a mesma com uma pequena diferença. Devemos percorrer até o nó final e, em seguida, percorrer do nó final até o nó frontal.
Aqui estão as etapas para percorrer uma lista duplamente vinculada desde o final:
Passo 1) Atravesse até chegar ao nó final.
Passo 2) A partir do nó final, percorreremos até obtermos o nó anterior como “NULL”. O “prev” (ponteiro anterior) será nulo para o nó principal
Passo 3) A cada iteração, imprimiremos os dados do nó.
Aqui está o pseudocódigo para percorrer de trás:
function traverseFromBack(ListHead): head = ListHead while head not equals NULL: head = head.next tail = head while tail not equal to NULL: print tail.value tail = tail.prev return ListHead
Diferença entre lista vinculada simples e duplamente
A principal diferença entre a lista individual e duplamente vinculada é o número de links.
Aqui está a diferença entre os nós de uma lista vinculada individualmente e a estrutura de nós da lista duplamente vinculada:
| Campo | Lista encadeada individualmente | Lista duplamente vinculada |
|---|---|---|
| Estrutura | Lista encadeada individualmente tem um campo de dados e um link para o próximo nó. | A lista duplamente vinculada possui um campo de dados e dois links. Um para o nó anterior e outro para o próximo nó. |
| Traversal | Ele só pode percorrer da cabeça à cauda. | Ele pode percorrer tanto para frente quanto para trás. |
| Memória | Ocupa menos memória. | Ocupa mais memória do que uma lista vinculada individualmente. |
| Acessibilidade | Listas vinculadas individualmente são menos eficientes porque usam apenas um link para o próximo nó. Não há link para o nó anterior. | As listas duplamente vinculadas são mais eficientes do que as listas vinculadas individualmente. |
Lista Duplamente Vinculada em C++
#include<iostream>
using namespace std;
struct node{
int data;
struct node *next;
struct node *prev;
};
void insertFront(node* &listHead, int value){
node* newNode = new node();
newNode->data = value;
newNode->prev = NULL;
newNode->next = NULL;
if(listHead!=NULL)
{
listHead->prev = newNode;
newNode->next = listHead;
}
listHead = newNode;
cout<<"Added "<<value<<" at the front"<<endl;
}
void insertEnd(node * &listHead, int value){
if(listHead==NULL)
{
insertFront(listHead,value);
}
node* newNode = new node();
newNode->data = value;
newNode->prev = NULL;
newNode->next = NULL;
node *head = listHead;
while(head->next!=NULL){
head = head->next;
}
head->next = newNode;
newNode->prev = head;
cout<<"Added "<<value<<" at the end"<<endl;
}
void insertAfter(node * &listHead, int searchValue,int value){
node* newNode = new node();
newNode->data = value;
newNode->prev = NULL;
newNode->next = NULL;
node *head = listHead;
while(head->next!=NULL && head->data!=searchValue){
head = head->next;
}
newNode->next = head->next;
head->next = newNode;
newNode->prev = head;
if(newNode->next !=NULL){
newNode->next->prev = newNode;
}
cout<<"Inserted "<<value<<" after node "<<searchValue<<endl;
}
void insertBefore(node * &listHead, int searchValue,int value){
node* newNode = new node();
newNode->data = value;
newNode->prev = NULL;
newNode->next = NULL;
node *head = listHead;
while(head->next!=NULL && head->next->data!=searchValue){
head = head->next;
}
newNode->next = head->next;
head->next = newNode;
newNode->prev = head;
if(newNode->next !=NULL){
newNode->next->prev = newNode;
}
cout<<"Inserted "<<value<<" before node "<<searchValue<<endl;
}
void traverseFromFront(node *listHead){
node* head = new node();
head = listHead;
cout<<"Traversal from head:\t";
while(head!=NULL){
cout<<head->data<<"\t ";
head = head->next;
}
cout<<endl;
}
void traverseFromEnd(node *listHead){
node* head = new node();
head = listHead;
cout<<"Traversal from head:\t";
while(head->next!=NULL){
head = head->next;
}
node *tail = head;
while(tail!=NULL){
cout<<tail->data<<"\t";
tail = tail->prev;
}
cout<<endl;
}
void searchAndDelete(node **listHead, int searchItem){
node* head = new node();
head = (*listHead);
while(head->next!=NULL && head->data!=searchItem){
head = head->next;
}
if(*listHead == NULL || head == NULL) return;
if((*listHead)->data == head->data){
*listHead = head->next;
}
if(head->next != NULL){
head->next->prev = head->prev;
}
if(head->prev != NULL){
head->prev->next = head->next;
}
free(head);
cout<<"Deleted Node\t"<<searchItem<<endl;
return;
}
int main(){
node *head = NULL;
insertFront(head,5);
insertFront(head,6);
insertFront(head,7);
insertEnd(head,9);
insertEnd(head,10);
insertAfter(head,5,11);
insertBefore(head,5,20);
traverseFromFront(head);
traverseFromEnd(head);
searchAndDelete(&head,7);
traverseFromFront(head);
traverseFromEnd(head);
}
saída
Added 5 at the front Added 6 at the front Added 7 at the front Aded 9 at the end Added 10 at the end Inserted 11 after node 5 Inserted 20 before node 5 Traversal from head: 7 6 20 5 11 9 10 Traversal from head: 10 9 11 5 20 6 7 Deleted Node 7 Traversal from head: 6 20 5 11 9 10 Traversal from head: 10 9 11 5 20 6
Lista Duplamente Vinculada em Python
class node:
def __init__(self,data = None, prev=None, next = None):
self.data = data
self.next = next
self.prev = prev
class DoublyLinkedList:
def __init__(self):
self.head = None
def insertFront(self, val):
newNode = node(data=val)
newNode.next = self.head
if self.head is not None:
self.head.prev = newNode
self.head = newNode
print("Added {} at the front".format(val))
def insertEnd(self,val):
newNode = node(data=val)
if self.head is None:
self.insertFront(val)
temp = self.head
while temp.next is not None:
temp = temp.next
temp.next = newNode
newNode.prev = temp
print("Added {} at the end".format(val))
def traverseFromFront(self):
temp = self.head
print("Traversing from head:\t",end="")
while temp:
print("{}\t".format(temp.data),end="")
temp = temp.next
print()
def traverseFromEnd(self):
temp = self.head
print("Traversing from Tail:\t",end="")
while temp.next is not None:
temp = temp.next
tail = temp
while tail is not None:
print("{}\t".format(tail.data),end="")
tail = tail.prev
print()
def insertAfter(self,searchItem, value):
newNode = node(data=value)
temp = self.head
while temp.next is not None and temp.data is not searchItem:
temp = temp.next
newNode.next = temp.next
temp.next = newNode
newNode.prev = temp
if newNode.next is not None:
newNode.next.prev = newNode
print("Inserted {} after node {}".format(value,searchItem))
def insertBefore(self,searchItem, value):
newNode = node(data=value)
temp = self.head
while temp.next is not None and temp.next.data is not searchItem:
temp = temp.next
newNode.next = temp.next
temp.next = newNode
newNode.prev = temp
if newNode.next is not None:
newNode.next.prev = newNode
print("Inserted {} before node {}".format(value,searchItem))
def searchAndDelete(self,searchItem):
temp = self.head
while temp.next is not None and temp.data is not searchItem:
temp = temp.next
if self.head is None or temp is None:
return
if self.head.data is temp.data:
self.head = temp.next
if temp.next is not None:
temp.next.prev = temp.prev
if temp.prev is not None:
temp.prev.next = temp.next
print("Deleted Node\t{}".format(searchItem))
return
doublyLinkedList = DoublyLinkedList()
doublyLinkedList.insertFront(5)
doublyLinkedList.insertFront(6)
doublyLinkedList.insertFront(7)
doublyLinkedList.insertEnd(9)
doublyLinkedList.insertEnd(10)
doublyLinkedList.insertAfter(5, 11)
doublyLinkedList.insertBefore(5, 20)
doublyLinkedList.traverseFromFront()
doublyLinkedList.traverseFromEnd()
doublyLinkedList.searchAndDelete(7)
doublyLinkedList.traverseFromFront()
doublyLinkedList.traverseFromEnd()
saída
Added 5 at the front Added 6 at the front Added 7 at the front Added 9 at the end Added 10 at the end Inserted 11 after node 5 Inserted 20 before node 5 Traversing from head: 7 6 20 5 11 9 10 Traversing from Tail: 10 9 11 5 20 6 7 Deleted Node 7 Traversing from head: 6 20 5 11 9 10 Traversing from Tail: 10 9 11 5 20 6
Complexidade da lista duplamente vinculada
Geralmente, a complexidade do tempo é dividida em três tipos.
Eles são:
- Melhor Case
- Caso Médio
- Pior caso
Complexidade de tempo no melhor caso para lista duplamente vinculada:
- A inserção na cabeça ou na cauda custará O(1). Porque não precisamos percorrer a lista vinculada. O ponteiro inicial e final podem nos dar acesso ao nó principal e final com complexidade de tempo O (1).
- A exclusão no início ou no final custará O(1).
- A pesquisa de um nó terá a complexidade de tempo de O(1). Porque o nó de destino pode ser o nó principal.
Complexidade de tempo no caso médio para lista duplamente vinculada:
- A inserção na cabeça ou na cauda terá a complexidade de tempo do custo O(1).
- A exclusão no início ou no final terá a complexidade de tempo do custo O(1).
- A pesquisa de um nó terá a complexidade de tempo de O(n). Porque o item de pesquisa pode residir em qualquer lugar da lista vinculada. Aqui, “n” é o nó total presente na lista vinculada.
A complexidade de tempo do pior caso da lista duplamente vinculada será a mesma do caso médio.
Complexidade de memória da lista duplamente vinculada
A complexidade da memória é O(n), onde “n” é o número total de nós. Ao implementar a lista vinculada, devemos liberar a memória que usamos. Caso contrário, para uma lista vinculada maior, causará vazamento de memória.
Resumo
- Uma lista vinculada é um tipo de estrutura de dados linear, uma coleção de dados representados de maneira linear.
- Uma lista duplamente vinculada é um tipo de lista vinculada em que um nó possui links com o nó anterior e o próximo.
- A lista duplamente vinculada contém todas as operações como adicionar um nó, excluir um nó, inserir um nó antes ou depois de outro nó e percorrer a lista vinculada do início ao fim.
- A lista duplamente vinculada possui um campo de dados e dois links. Um para o nó anterior e outro para o próximo nó.
- Complexidade da lista duplamente encadeada Melhor Caso, Caso médio
- E o pior caso.



