O nó final de uma lista vinculada que possui um loop se referirá a outro nó na mesma lista, em vez do nulo.Se houver um nó em uma lista vinculada que possa ser acessada repetidamente seguindo o próximo ponteiro, diz -se que a lista tem um ciclo.
Normalmente, o último nó da lista vinculada refere -se a uma referência nula para denotar a conclusão da lista. No entanto, em uma lista vinculada com um loop, o nó final da lista refere -se a um nó inicial, um nó interno ou em si mesmo. Portanto, em tais circunstâncias, devemos identificar e encerrar o loop, definindo a referência do próximo nó a nulo. A parte de detecção do loop em uma lista vinculada é explicada neste artigo.
Em C ++, existem inúmeras maneiras de encontrar loops em uma lista vinculada:
Abordagem baseada na mesa de hash: Esta abordagem armazena os endereços dos nós visitados em uma tabela de hash. Existe um loop na lista vinculada se um nó já estiver presente na tabela de hash quando for visitado novamente.
Abordagem de ciclo de Floyd: O algoritmo “Tartaruga e lebre”, comumente conhecido como algoritmo de encontrar ciclo de Floyd: essa técnica faz uso de dois ponteiros, um se movendo mais lentamente do que o outro e o outro se movendo mais rapidamente. O ponteiro mais rápido acabará por ultrapassar o ponteiro mais lento se houver um loop na lista vinculada, revelando a existência do loop.
Método recursivo: Este método passa pela lista vinculada ligando -se repetidamente. A lista vinculada contém um loop se o nó atual foi visitado anteriormente.
Abordagem baseada em pilha: Esta abordagem armazena os endereços dos nós visitados em uma pilha. Um loop na lista vinculado está presente se um nó já estiver lá na pilha quando for visitado novamente.
Vamos explicar cada abordagem em detalhes para entender o conceito.
Abordagem 1: abordagem de hashset
Utilizar o hash é o método mais direto. Aqui, passamos pela lista um por um enquanto mantemos uma tabela de hash com os endereços do nó. Portanto, existe um loop se algum dia encontrarmos o próximo endereço do nó atual que já está contido na tabela de hash. Caso contrário, não há loop na lista vinculada se encontrarmos null (i.e., alcançar o final da lista vinculada).
Será muito fácil implementar esta estratégia.
Ao atravessar a lista vinculada, utilizaremos um UNODEDED_HASHMAP e continuamos adicionando nós a ela.
Agora, uma vez que encontramos um nó que já é mostrado no mapa, saberemos que chegamos ao começo do loop.
Ao fazer isso, o nó do laço final, em vez de se referir ao nó inicial do loop, começa a apontar para NULL.
O tempo médio e a complexidade do espaço do método de hash é O (n). O leitor deve sempre estar ciente de que a implementação daestrutura de dados de hash interno na linguagem de programação determinará a complexidade total do tempo no caso de uma colisão no hashing.
Implementação do programa C ++ para o método acima (hashset):
#incluir
usando namespace std;
Nó da estrutura
int valor;
Nó da estrutura* Em seguida;
;
Nó* newNode (int valor)
Nó* tempnode = novo nó;
tempnode-> value = value;
tempnode-> próximo = null;
retornar tempnode;
// Identifique e elimine qualquer potencial loops
// em uma lista vinculada com esta função.
Void functionHashmap (nó* HeadNode)
// criou um não -ordered_map para implementar o mapa de hash
UNODERED_MAPhash_map;
// Ponteiro para Lugarnode
Nó* lastNode = null;
while (HeadNode != Null)
// Se falta um nó no mapa, adicione -o.
if (hash_map.encontre (headNode) == hash_map.fim())
hash_map [headNode] ++;
lastNode = headNode;
HeadNode = HeadNode-> Next;
// Se um ciclo estiver presente, defina o próximo ponteiro do nó final como nulo.
outro
lastNode-> próximo = null;
quebrar;
// Lista vinculada de exibição
Void Display (nó* HadNode)
while (HeadNode != Null)
coutHeadNode = HeadNode-> Next;
cout << endl;
/* Função principal*/
int main ()
Nó* headNode = newNode (48);
HeadNode-> Next = HeadNode;
HeadNode-> Next = newNode (18);
HeadNode-> Next-> Next = newNode (13);
HeadNode-> Next-> Next-> Next = newNode (2);
HeadNode-> Next-> Next-> Next-> Next = newNode (8);
/ * Crie um loop no LinkedList */
HeadNode-> Next-> Next-> Next-> Next-> Next = headNode-> Next-> Next;
functionHashMap (HeadNode);
printf ("Lista vinculada sem loop \ n");
tela (headNode);
retornar 0;
Saída:
Lista vinculada sem loop
48 18 13 2 8
A explicação passo a passo do código é fornecida abaixo:
Abordagem 2: Ciclo de Floyd
O algoritmo de detecção de ciclo de Floyd, muitas vezes conhecido como Tartaruga e Algoritmo Hare, fornece a base para esse método de localizar ciclos em uma lista vinculada. O ponteiro "lento" e o ponteiro "rápido", que atravessam a lista em várias velocidades, são os dois ponteiros usados nesta técnica. O ponteiro rápido avança duas etapas, enquanto o ponteiro lento avança um passo a cada iteração. Existe um ciclo na lista vinculada se os dois pontos ficaram cara a cara.
1. Com o nó da cabeça da lista vinculada, inicializamos dois ponteiros chamados.
2. Agora corremos um loop para passar pela lista vinculada. O ponteiro rápido deve ser movido dois locais em frente ao ponteiro lento na etapa de cada iteração.
3. Não haverá um loop na lista vinculada se o ponteiro rápido chegar ao final da lista (FastPointer == NULL ou FASTPOINTER-> NEXT == NULL). Caso contrário, os ponteiros rápidos e lentos acabarão se encontrando, o que significa que a lista vinculada tem um loop.
Implementação do programa C ++ para o método acima (ciclo de Floyd):
#incluir
usando namespace std;
/ * Link list node */
Nó da estrutura
int dados;
Nó da estrutura* Em seguida;
;
/* Função para remover o loop. */
vazio deleteeloop (nó da estrutura*, nó da estrutura*);
/* Esta função localiza e elimina loops de lista. Ele produz 1
se houvesse um loop na lista; caso contrário, ele retorna 0. */
int DetectAndDeleteLoop (Lista de Nó da estrutura*)
Nó da estrutura *lentaptr = list, *fastptr = list;
// itera para verificar se o loop está lá.
while (SlowPtr && FastPtr && FastPtr-> Next)
SlowPtr = SlowPtr-> Em seguida;
fastptr = fastptr-> próximo-> próximo;
/* Se SlowPtr e FastPtr se encontrarem em algum momento, então lá
é um loop */
if (SlowPtr == FastPtr)
deleteeloop (lenta, lista);
/* Retorne 1 para indicar que um loop foi descoberto. */
retornar 1;
/* Retorne 0 para indicar que nenhum loop foi descoberto.*/
retornar 0;
/* Função para excluir loop da lista vinculada.
O loopnode está apontando para um dos nós de loop e a cabeça está apontando
para o nó inicial da lista vinculada */
Void Deleteloop (nó da estrutura* Loopnode, Nó da estrutura* HadNode)
Nó da estrutura* ptr1 = loopnode;
Nó da estrutura* ptr2 = loopnode;
// Conte quantos nós estão no loop.
não assinado int k = 1, i;
while (ptr1-> próximo != ptr2)
ptr1 = ptr1-> próximo;
k ++;
// conserte um ponteiro para o cabeçote
pTr1 = HeadNode;
// e o outro ponteiro para k nós após o código do cabeçote
pTr2 = HeadNode;
para (i = 0; i < k; i++)
ptr2 = ptr2-> próximo;
/* Quando os dois pontos são movidos simultaneamente,
Eles vão colidir no nó inicial do loop. */
while (ptr2 != ptr1)
ptr1 = ptr1-> próximo;
ptr2 = ptr2-> próximo;
// Obtenha o último ponteiro do nó.
while (ptr2-> próximo != ptr1)
ptr2 = ptr2-> próximo;
/* Para fechar o loop, defina o subsequente
nó para o nó terminante do loop. */
ptr2-> próximo = nulo;
/ * Função para exibir lista vinculada */
Void DisplayLinkedList (nó da estrutura* nó)
// exibe a lista vinculada após excluir o loop
while (nó != Null)
cout nó = nó-> a seguir;
Nó da estrutura* newNode (tecla int)
Nó da estrutura* temp = new Node ();
temp-> data = key;
temp-> próximo = nulo;
retornar temp;
// Código principal
int main ()
Nó da estrutura* HeadNode = newNode (48);
HeadNode-> Next = newNode (18);
HeadNode-> Next-> Next = newNode (13);
HeadNode-> Next-> Next-> Next = newNode (2);
HeadNode-> Next-> Next-> Next-> Next = newNode (8);
/ * Crie um loop */
HeadNode-> Next-> Next-> Next-> Next-> Next = headNode-> Next-> Next;
// exibe o loop na lista vinculada
// DisplayLinkedList (HeadNode);
DetectandDeleteLoop (HeadNode);
cout << "Linked List after no loop \n";
DisplayLinkedList (HeadNode);
retornar 0;
Saída:
Lista vinculada após nenhum loop
48 18 13 2 8
Explicação:
Abordagem 3: Recursão
Recursão é uma técnica para resolver problemas, particionando -os em subproblemas menores e mais fáceis. A recursão pode ser usada para atravessar uma lista individual, caso um loop seja encontrado executando continuamente uma função para o próximo nó na lista até que o final da lista seja alcançado.
Em uma lista individual, o princípio básico por trás do uso da recursão para encontrar um loop é começar à frente da lista, marcar o nó atual, conforme visitado em cada etapa e depois continuar para o próximo nó, invocando recursivamente a função para a função para aquele nó. O método irá atingir a lista completa vinculada porque é chamada recursivamente.
A lista contém um loop se um nó que foi marcado anteriormente como visitado for encontrado pela função; Nesse caso, a função pode retornar verdadeiro. O método pode retornar falso se chegar ao final da lista sem correr em um nó visitado, o que indica que não há loop.
Embora essa técnica para utilizar a recursão para encontrar um loop em uma única lista vinculada seja direta para usar e compreender, pode não ser a mais eficaz em termos de tempo e complexidade do espaço.
Implementação do programa C ++ para o método acima (recursão):
#incluir
usando namespace std;
Nó da estrutura
int dados;
Nó* a seguir;
Bool visitou;
;
// Função de detecção de loop de lista vinculada
Bool DetectLoOlinkedList (nó* HeadNode)
if (headNode == null)
retorna falso; // Se a lista vinculada estiver vazia, o caso básico
// existe um loop se o nó atual tiver
// já foi visitado.
if (headNode-> visitado)
retornar true;
// Adicione uma marca de visita ao nó atual.
headNode-> visitou = true;
// chamando o código para o nó subsequente repetidamente
return detectlooplinkedlist (headnode-> próximo);
int main ()
Nó* headNode = new Node ();
Nó* SecondNode = new Node ();
Nó* terceironode = new Node ();
headNode-> dados = 1;
HeadNode-> Next = SecondNode;
headNode-> visitou = false;
SecondNode-> dados = 2;
SecondNode-> Next = ThirdNode;
SecondNode-> visitado = false;
terceironode-> dados = 3;
terceironode-> próximo = nulo; // sem loop
terceironode-> visitado = false;
if (detectlooplinkedlist (headnode))
cout << "Loop detected in linked list" << endl;
outro
cout << "No loop detected in linked list" << endl;
// Criando um loop
terceironode-> next = SecondNode;
if (detectlooplinkedlist (headnode))
cout << "Loop detected in linked list" << endl;
outro
cout << "No loop detected in linked list" << endl;
retornar 0;
Saída:
Nenhum loop detectado na lista vinculada
Loop detectado na lista vinculada
Explicação:
Abordagem 4: Usando a pilha
Loops em uma lista vinculada podem ser encontrados usando uma pilha e o método "Pesquisa de profundidade primeiro" (DFS). O conceito fundamental é iterar através da lista vinculada, empurrando cada nó para a pilha, se ainda não foi visitado. Um loop é reconhecido se um nó que já estiver na pilha for encontrado novamente.
Aqui está uma breve descrição do procedimento:
Implementação do programa C ++ para o método acima (pilha)
#incluir
#incluir
usando namespace std;
Nó da estrutura
int dados;
Nó* a seguir;
;
// Função para detectar loop em uma lista vinculada
Bool DetectLoOlinkedList (nó* HeadNode)
pilhapilha;
Nó* tempnode = headNode;
enquanto (tempnode != Null)
// se o elemento superior da pilha corresponder
// O nó atual e a pilha não estão vazios
se (!pilha.vazio () && pilha.top () == tempnode)
retornar true;
pilha.push (tempnode);
tempnode = tempnode-> próximo;
retorna falso;
int main ()
Nó* headNode = new Node ();
Nó* SecondNode = new Node ();
Nó* terceironode = new Node ();
headNode-> dados = 1;
HeadNode-> Next = SecondNode;
SecondNode-> dados = 2;
SecondNode-> Next = ThirdNode;
terceironode-> dados = 3;
terceironode-> próximo = nulo; // sem loop
if (detectlooplinkedlist (headnode))
cout << "Loop detected in linked list" << endl;
outro
cout << "No loop detected in linked list" << endl;
// Criando um loop
terceironode-> next = SecondNode;
if (detectlooplinkedlist (headnode))
cout << "Loop detected in linked list" << endl;
outro
cout << "No loop detected in linked list" << endl;
Saída:
Nenhum loop detectado na lista vinculada
Loop detectado na lista vinculada
Explicação:
Este programa usa uma pilha para descobrir se uma lista ligada individual tem um loop.
2. O espaço para nome padrão está incluído na segunda linha, permitindo -nos acessar as funções da biblioteca de fluxo de entrada/saída sem precisar prefixá -las com “STD ::.”
3. A linha a seguir define o nó da estrutura, que consiste em dois membros: um número inteiro chamado "dados" e um ponteiro para outro nó chamado "Próximo.”
4. A cabeça da lista vinculada é uma entrada para o método de detectlooplinkedlist (), que é definido na próxima linha. A função produz um valor booleano que indica se um loop foi ou não encontrado.
5. Uma pilha de ponteiros de nós chamados "pilha" e um ponteiro para um nó chamado "tempnode" que é inicializado com o valor do nó de cabeça são criados dentro da função.
6. Então, desde que o tempnode não seja um ponteiro nulo, entramos um pouco.
(a) O elemento superior da pilha deve corresponder ao nó atual para que determos que não está vazio. Retornamos verdadeiro se esse for o caso porque um loop foi encontrado.
(b) Se a condição acima mencionada for falsa, o ponteiro do nó atual será empurrado para a pilha e o tempnode é definido para o seguinte nó na lista vinculada.
7. Voltamos falsos após o loop while, porque nenhum loop foi observado.
8. Construímos três objetos de nós e os iniciamos na função principal (). Como não há loop no primeiro exemplo, definimos os "próximos" ponteiros de cada nó corretamente.
Conclusão:
Em conclusão, o melhor método para detectar loops em uma lista vinculada depende do caso de uso específico e das restrições do problema. A tabela de hash e o algoritmo de encontrar ciclo de Floyd são métodos eficientes e são amplamente utilizados na prática. Pilha e recursão são métodos menos eficientes, mas são mais intuitivos.