Lista vinculada individualmente em estruturas de dados

โšก Resumo Inteligente

Uma lista simplesmente encadeada รฉ uma estrutura de dados linear e unidirecional onde cada nรณ armazena dados e um รบnico ponteiro para o prรณximo nรณ, de modo que a travessia se move apenas da cabeรงa para a cauda e a memรณria รฉ alocada dinamicamente ร  medida que novos nรณs sรฃo adicionados.

  • ๐Ÿงฉ Estrutura do nรณ: Cada nรณ contรฉm um campo de dados e um Prรณximo ponteiro para o nรณ seguinte; o nรณ final do nรณ Prรณximo O ponteiro รฉ NULL.
  • ๐Ÿ“ฆ Lista vs. Array: Listas simplesmente encadeadas sรฃo preferรญveis quando o nรบmero de elementos รฉ desconhecido, o acesso aleatรณrio nรฃo รฉ necessรกrio e a inserรงรฃo no meio da lista รฉ comum.
  • โž• Inserรงรตes: ร‰ possรญvel adicionar nรณs no inรญcio, no final, apรณs um nรณ correspondente ou antes de um nรณ correspondente usando reescritas de ponteiro seguinte.
  • โž– Exclusรตes: Remover o nรณ inicial, o nรณ final ou um nรณ pesquisado atualiza os ponteiros dos vizinhos e libera a memรณria liberada para evitar vazamentos.
  • ๐Ÿ” Travessia: Apenas a travessia direta รฉ suportada, pois nรฃo hรก ponteiro anterior; portanto, percorrer uma lista simplesmente encadeada na ordem inversa nรฃo รฉ possรญvel.
  • ๐Ÿ’ป C++ e Python Code: Implementaรงรตes completas mostram rotinas de inserรงรฃo, exclusรฃo, busca e percurso com resultados executรกveis.
  • ๐Ÿ“Š Complexidade: A inserรงรฃo ou remoรงรฃo da cabeรงa รฉ O(1); a busca e outras inserรงรตes e remoรงรตes sรฃo O(n); a complexidade de espaรงo รฉ O(n).

Lista encadeada individualmente

O que รฉ uma lista vinculada individualmente?

Uma lista simplesmente encadeada รฉ uma estrutura de dados linear e unidirecional onde os dados sรฃo armazenados nos nรณs, e cada nรณ รฉ conectado ao nรณ seguinte por meio de uma ligaรงรฃo. Cada nรณ contรฉm um campo de dados e uma ligaรงรฃo para o prรณximo nรณ. Listas simplesmente encadeadas podem ser percorridas em apenas uma direรงรฃo, enquanto uma lista nรฃo encadeada pode ser percorrida em uma รบnica direรงรฃo. Lista duplamente vinculada Pode ser percorrido em ambas as direรงรตes.

Aqui estรก a estrutura de nรณs de uma Lista Simplesmente Encadeada:

Estrutura de um nรณ em uma lista vinculada

Estrutura de um nรณ em uma lista vinculada

Por que usar uma lista encadeada em vez de um array?

Diversos cenรกrios favorecem uma Lista Ligada em detrimento de uma Lista Encadeada. Ordem:

  • Nรบmero desconhecido de elementos: Quando a quantidade de elementos necessรกria nรฃo รฉ conhecida em tempo de compilaรงรฃo, uma Lista Ligada aloca memรณria dinamicamente ร  medida que os elementos sรฃo adicionados.
  • Acesso aleatรณrio: Quando o acesso indexado aleatรณrio nรฃo รฉ necessรกrio, uma lista encadeada รฉ uma escolha adequada.
  • Inserรงรฃo no meio: Inserir um elemento no meio de um array requer o deslocamento de elementos. Uma lista ligada permite a inserรงรฃo em qualquer posiรงรฃo, reescrevendo apenas alguns ponteiros.

Operaรงรตes de lista vinculada individualmente

Uma lista simplesmente encadeada รฉ ideal para alocaรงรฃo dinรขmica de memรณria. Ela suporta as operaรงรตes padrรฃo de uma lista encadeada, como inserรงรฃo, remoรงรฃo, busca, atualizaรงรฃo, fusรฃo de duas listas e percurso.

As seguintes operaรงรตes sรฃo discutidas neste artigo:

  • Inserindo na cabeรงa
  • Inserindo na cauda
  • Inserindo apรณs um nรณ
  • Inserindo antes de um nรณ
  • Exclua o nรณ principal
  • Exclua o nรณ final
  • Pesquise e exclua um nรณ
  • Percorrendo a lista vinculada

Aqui estรก um exemplo de uma lista encadeada com quatro nรณs.

Exemplo de uma lista vinculada individualmente

Exemplo de uma lista vinculada individualmente

Inserรงรฃo no cabeรงalho de uma lista simplesmente encadeada

Esta รฉ uma operaรงรฃo simples. Geralmente รฉ conhecida como inserรงรฃo em uma lista simplesmente encadeada. Um novo nรณ รฉ criado e colocado no inรญcio da lista.

Para realizar esta operaรงรฃo, observe duas condiรงรตes importantes:

  1. Se a lista estiver vazia, o nรณ recรฉm-criado torna-se o nรณ principal e seu Prรณximo O ponteiro รฉ NULL.
  2. Se a lista nรฃo estiver vazia, o novo nรณ se torna o nรณ principal e seu Prรณximo O ponteiro aponta para o nรณ principal anterior.

Segue o pseudocรณdigo para inserir um nรณ no inรญcio de uma lista encadeada:

function insertAtHead(head, value):
  newNode = Node(value)
  if head is NULL:
    head = newNode
    return head
  else:
    newNode.next = head
    return newNode

Inserindo na cabeรงa

Inserindo na cabeรงa

Inserรงรฃo no final de uma lista simplesmente encadeada

Inserir um nรณ no final de uma lista encadeada รฉ semelhante a inserir no inรญcio. Percorra a lista atรฉ o nรณ final e, em seguida, aponte para ele. Prรณximo Ponteiro para o novo nรณ. Se o cabeรงalho for NULL, o novo nรณ se torna o cabeรงalho.

Passo 1) Atravesse atรฉ o Prรณximo O ponteiro do nรณ atual torna-se NULL.

Passo 2) Crie um novo nรณ com o valor especificado.

Passo 3) Atribua o novo nรณ como o prรณximo nรณ do nรณ final.

O pseudocรณdigo para inserir um elemento no final de uma lista com apenas um elemento:

function insertAtEnd(head, value):
  newNode = Node(value)
  if head is NULL:
    head = newNode
    return head
  while head.next is not NULL:
    head = head.next
  head.next = newNode
  newNode.next = NULL

Inserindo na cauda

Inserindo na cauda

Inserรงรฃo apรณs um nรณ em uma lista simplesmente encadeada

A inserรงรฃo apรณs um nรณ envolve duas partes: buscar o nรณ alvo e anexar um novo nรณ apรณs ele. Percorrer a lista atรฉ encontrar uma correspondรชncia e, em seguida, inserir o novo nรณ.

Passo 1) Percorra a lista atรฉ que o valor do nรณ atual seja igual ao item de busca.

Passo 2) Defina o novo nรณ Prรณximo ponteiro para o nรณ atual Prรณximo ponteiro.

Passo 3) Aponte o nรณ atual para Prรณximo ponteiro para o novo nรณ.

Pseudocรณdigo:

function insertAfter(head, value, searchItem):
  newNode = Node(value)
  while head.value != searchItem:
    head = head.next
  newNode.next = head.next
  head.next = newNode

Inserindo um nรณ apรณs um nรณ em uma lista vinculada individualmente

Inserindo um nรณ apรณs um nรณ na lista vinculada individualmente

Inserรงรฃo antes de um nรณ em uma lista simplesmente encadeada

Isso รฉ semelhante ร  inserรงรฃo apรณs um nรณ. Percorra a lista atรฉ encontrar o prรณximo nรณ que corresponda ao valor de busca e, em seguida, insira o novo nรณ antes dele.

Passo 1) Percorra atรฉ que o valor do prรณximo nรณ seja igual ao item de pesquisa.

Passo 2) Crie um novo nรณ e defina-o Prรณximo ponteiro para o nรณ atual Prรณximo.

Passo 3) Aponte o nรณ atual para Prรณximo para o novo nรณ.

function insertBefore(head, value, searchItem):
  newNode = Node(value)
  while head.next.value != searchItem:
    head = head.next
  newNode.next = head.next
  head.next = newNode

Inserindo um nรณ antes de um nรณ em uma lista vinculada individualmente

Inserindo um nรณ antes de um nรณ na lista vinculada individualmente

Excluir o cabeรงalho da lista simplesmente encadeada

O ponteiro para o nรณ inicial รฉ fornecido como parรขmetro. O nรณ inicial รฉ removido e o prรณximo nรณ se torna o novo nรณ inicial. A memรณria do nรณ excluรญdo deve ser liberada para evitar vazamentos de memรณria.

Passo 1) Atribua o prรณximo nรณ da cabeรงa como a nova cabeรงa.

Passo 2) Libere a memรณria alocada do nรณ cabeรงalho anterior.

Passo 3) Retorne o novo nรณ principal.

function deleteHead(head):
  temp = head
  head = head.next
  free(temp)
  return head

Excluindo o cabeรงalho de uma lista vinculada

Excluindo o cabeรงalho de uma lista vinculada

Excluir a cauda da lista simplesmente encadeada

Excluir o nรณ final รฉ semelhante a excluir o nรณ inicial. A diferenรงa รฉ que รฉ necessรกrio percorrer toda a lista atรฉ o final. Em uma lista simplesmente encadeada, o nรณ cujo Prรณximo O ponteiro รฉ NULL e รฉ o nรณ final.

Passo 1) Percorra o trajeto atรฉ um pouco antes do nรณ final. Salve o nรณ atual.

Passo 2) Libere a memรณria do prรณximo nรณ (a cauda).

Passo 3) Defina o prรณximo nรณ do nรณ atual como NULL.

function deleteTail(head):
  while head.next.next is not NULL:
    head = head.next
  free(head.next)
  head.next = NULL

Excluindo o final da lista vinculada individualmente

Excluindo o final da lista vinculada individualmente

Pesquisar e excluir um nรณ de uma lista simplesmente encadeada

Esta funรงรฃo executa duas tarefas: busca e exclusรฃo. Percorre a lista atรฉ o final. Se um nรณ correspondente for encontrado, remove-o e religa o nรณ anterior. Prรณximo ponteiro.

Passo 1) Percorra a lista atรฉ o final. Verifique se o nรณ atual รฉ igual ao nรณ de busca.

Passo 2) Se uma correspondรชncia for encontrada, armazene um ponteiro para o nรณ atual.

Passo 3) O Prรณximo O nรณ anterior torna-se o prรณximo nรณ do nรณ atual.

Passo 4) Exclua o nรณ atual e libere sua memรณria.

function searchAndDelete(head, searchItem):
  while head.next.next is not NULL and head.next.value != searchItem:
    head = head.next
  temp = head.next
  head.next = head.next.next
  free(temp)

Pesquise e exclua um nรณ da lista vinculada individualmente

Pesquise e exclua um nรณ da lista vinculada individualmente

Percorrer uma lista simplesmente encadeada

Uma lista simplesmente encadeada suporta apenas a travessia do inรญcio ao fim. Nรฃo hรก ponteiro para o nรณ anterior, portanto a travessia reversa nรฃo รฉ possรญvel. Cada nรณ รฉ visitado sequencialmente, imprimindo seu valor atรฉ que NULL seja alcanรงado.

Passo 1) Percorra cada nรณ atรฉ encontrar NULL.

Passo 2) Imprima o valor do nรณ atual.

function traverse(head):
  while head is not NULL:
    print head.value
    head = head.next

Exemplo de lista vinculada individualmente em C++

#include<iostream>
using namespace std;
struct Node{
  int data;
  struct Node *next;
};
void insertAtHead(Node* &head, int value){
  Node* newNode = new Node();
  newNode->data = value;
  newNode->next = NULL;
  if(head != NULL){
    newNode->next = head;
  }
  head = newNode;
  cout<<"Added "<<newNode->data<<" at the front"<<endl;
}
void insertEnd(Node* &head, int value){
  if(head == NULL){
    insertAtHead(head, value);
    return;
  }
  Node* newNode = new Node();
  newNode->data = value;
  newNode->next = NULL;
  Node *temp = head;
  while(temp->next != NULL){
    temp = temp->next;
  }
  temp->next = newNode;
  cout<<"Added "<<newNode->data<<" at the end"<<endl;
}
void searchAndDelete(Node **headPtr, int searchItem){
  Node *temp = NULL;
  if((*headPtr)->data == searchItem){
    temp = *headPtr;
    *headPtr = (*headPtr)->next;
    free(temp);
  } else {
    Node *currentNode = *headPtr;
    while(currentNode->next != NULL){
      if(currentNode->next->data == searchItem){
        temp = currentNode->next;
        currentNode->next = currentNode->next->next;
        free(temp);
        break;
      } else {
        currentNode = currentNode->next;
      }
    }
  }
  cout<<"Deleted Node\t"<<searchItem<<endl;
}
void insertAfter(Node* &headPtr, int searchItem, int value){
  Node* newNode = new Node();
  newNode->data = value;
  newNode->next = NULL;
  Node *head = headPtr;
  while(head->next != NULL && head->data != searchItem){
    head = head->next;
  }
  newNode->next = head->next;
  head->next = newNode;
  cout<<"Inserted "<<value<<" after node\t"<<searchItem<<endl;
}
void insertBefore(Node* &headPtr, int searchItem, int value){
  Node* newNode = new Node();
  newNode->data = value;
  newNode->next = NULL;
  Node *head = headPtr;
  while(head->next != NULL && head->next->data != searchItem){
    head = head->next;
  }
  newNode->next = head->next;
  head->next = newNode;
  cout<<"Inserted "<<value<<" before node\t"<<searchItem<<endl;
}
void traverse(Node *headPointer){
  Node* tempNode = headPointer;
  cout<<"Traversal from head:\t";
  while(tempNode != NULL){
    cout<<tempNode->data;
    if(tempNode->next)
      cout<<" --> ";
    tempNode = tempNode->next;
  }
  cout<<endl;
}
int main(){
  Node *head = NULL;
  insertAtHead(head, 5);
  insertAtHead(head, 6);
  insertAtHead(head, 7);
  insertEnd(head, 9);
  traverse(head);
  searchAndDelete(&head, 6);
  traverse(head);
  insertAfter(head, 7, 10);
  insertBefore(head, 9, 11);
  traverse(head);
}

saรญda

Added 5 at the front
Added 6 at the front
Added 7 at the front
Added 9 at the end
Traversal from head:    7 --> 6 --> 5 --> 9
Deleted Node    6
Traversal from head:    7 --> 5 --> 9
Inserted 10 after node  7
Inserted 11 before node 9
Traversal from head:    7 --> 10 --> 5 --> 11 --> 9

Exemplo de lista vinculada individualmente em Python

class Node:
  def __init__(self, data=None, next=None):
    self.data = data
    self.next = next
class SinglyLinkedList:
  def __init__(self):
    self.head = None
  def insertAtHead(self, value):
    newNode = Node(data=value)
    if self.head is not None:
      newNode.next = self.head
    self.head = newNode
    print(f'Added {newNode.data} at the front.')
  def insertAtEnd(self, value):
    if self.head is None:
      self.insertAtHead(value)
      return
    newNode = Node(value)
    temp = self.head
    while temp.next is not None:
      temp = temp.next
    temp.next = newNode
    print(f'Added {newNode.data} at the end.')
  def searchAndDelete(self, searchItem):
    if self.head is None:
      return
    if self.head.data == searchItem:
      self.head = self.head.next
      print(f'Deleted node\t{searchItem}')
      return
    currentNode = self.head
    while currentNode.next is not None:
      if currentNode.next.data == searchItem:
        currentNode.next = currentNode.next.next
        print(f'Deleted node\t{searchItem}')
        return
      currentNode = currentNode.next
  def insertAfter(self, searchItem, value):
    newNode = Node(data=value)
    temp = self.head
    while temp.next is not None and temp.data != searchItem:
      temp = temp.next
    newNode.next = temp.next
    temp.next = newNode
    print(f'Inserted {value} after node\t{searchItem}')
  def insertBefore(self, searchItem, value):
    newNode = Node(data=value)
    temp = self.head
    while temp.next is not None and temp.next.data != searchItem:
      temp = temp.next
    newNode.next = temp.next
    temp.next = newNode
    print(f'Inserted {value} before node\t{searchItem}')
  def traverse(self):
    temp = self.head
    print("Traversing from head:\t", end="")
    while temp:
      print("{}\t".format(temp.data), end="")
      temp = temp.next
    print()
singlyLinkedList = SinglyLinkedList()
singlyLinkedList.insertAtHead(5)
singlyLinkedList.insertAtHead(6)
singlyLinkedList.insertAtHead(7)
singlyLinkedList.insertAtEnd(9)
singlyLinkedList.traverse()
singlyLinkedList.searchAndDelete(6)
singlyLinkedList.traverse()
singlyLinkedList.insertAfter(7, 10)
singlyLinkedList.insertBefore(9, 11)
singlyLinkedList.traverse()

saรญda

Added 5 at the front.
Added 6 at the front.
Added 7 at the front.
Added 9 at the end.
Traversing from head:   7       6       5       9
Deleted node    6
Traversing from head:   7       5       9
Inserted 10 after node  7
Inserted 11 before node 9
Traversing from head:   7       10      5       11      9

Complexidade da lista vinculada individualmente

Existem dois tipos de complexidade: complexidade de tempo e complexidade de espaรงo. A complexidade de tempo no pior caso e no caso mรฉdio sรฃo as mesmas para uma lista simplesmente encadeada.

Complexidade de tempo no melhor caso:

  • A inserรงรฃo no inรญcio pode ser feita em O(1). Nรฃo รฉ necessรกrio percorrer a lista.
  • A busca e a exclusรฃo podem ser feitas em O(1) se o elemento alvo estiver no nรณ cabeรงa.

Complexidade de tempo no caso mรฉdio:

  • A inserรงรฃo dentro de uma lista ligada leva tempo O(n), onde n รฉ o nรบmero total de elementos.
  • A busca e a exclusรฃo tambรฉm podem levar O(n), porque o elemento alvo pode estar em qualquer lugar atรฉ o nรณ final.

Complexidade espacial de uma lista simplesmente encadeada

Uma lista simplesmente encadeada aloca memรณria dinamicamente. Para armazenar n elementos, ele aloca n unidades de memรณria. Portanto, a complexidade de espaรงo รฉ O(n).

Aplicaรงรตes de listas simplesmente encadeadas

Listas simplesmente encadeadas aparecem em muitos lugares onde a travessia apenas para frente e a memรณria dinรขmica sรฃo รบteis:

  • Pilhas e filas: Armazenamento subjacente para pilhas LIFO e filas FIFO construรญdas a partir de nรณs.
  • Encadeamento de tabelas hash: As colisรตes sรฃo resolvidas encadeando as entradas em uma Lista Simplesmente Encadeada por bucket.
  • Listas de adjacรชncia: Os grafos esparsos utilizam uma lista simplesmente encadeada de vizinhos para cada vรฉrtice.
  • Tabelas de sรญmbolos: Compiladores e interpretadores encadeiam identificadores em uma Lista Simplesmente Encadeada por escopo.
  • Alocadores de memรณria: alocadores de lista livre track blocos livres como uma lista simplesmente encadeada.

Perguntas Frequentes

Listas encadeadas simples (Singlely Linked Lists) encadeiam amostras de treinamento, mini-lotes e blocos de memรณria livre dentro de frameworks de IA, permitindo filas dinรขmicas para entradas de streaming e pipelines de dados sem bloqueio que escalam conforme a demanda do modelo.

Sim. O GitHub Copilot e o GPT podem gerar uma lista encadeada simples completa em C. C++, Java, Python, ou JavaScript, incluindo inserรงรฃo, exclusรฃo, reversรฃo, detecรงรฃo de ciclos e testes unitรกrios.

Uma lista simplesmente encadeada possui um ponteiro para o prรณximo nรณ e percorre apenas para frente. Uma lista duplamente encadeada possui ponteiros para o prรณximo e para o anterior e percorre os dois lados da lista, mas utiliza mais memรณria por nรณ.

Os usos comuns incluem implementaรงรตes de pilha e fila, encadeamento de tabelas hash, listas de adjacรชncia para grafos esparsos, tabelas de sรญmbolos em compiladores, alocadores de listas livres e histรณrico de desfazer em editores leves.

A inserรงรฃo ou remoรงรฃo no inรญcio รฉ O(1). A inserรงรฃo na cauda, โ€‹โ€‹a busca, a inserรงรฃo em uma posiรงรฃo e a remoรงรฃo de um nรณ especรญfico custam O(n) porque รฉ necessรกrio percorrer a lista a partir do inรญcio.

Listas encadeadas crescem e diminuem em tempo de execuรงรฃo, inserem ou removem em O(1) assim que a posiรงรฃo รฉ conhecida e nunca precisam de memรณria contรญgua. Arrays oferecem acesso aleatรณrio O(1) e melhor localidade de cache.

Percorra a lista com trรชs ponteiros: prev, curr e next. A cada passo, salve curr.next, aponte curr.next para prev e desloque prev e curr para a prรณxima posiรงรฃo. Retorne prev como o novo cabeรงalho.

O algoritmo da tartaruga e da lebre de Floyd usa dois ponteiros que se movem em velocidades diferentes. Se eles se encontrarem, a lista contรฉm um ciclo. Caso contrรกrio, o ponteiro mais rรกpido atinge NULL e nenhum ciclo existe.

Resuma esta postagem com: