Le nœud de fin d'une liste liée qui a une boucle se réfère à un autre nœud dans la même liste plutôt que le null.S'il y a un nœud dans une liste liée qui peut être accessible à plusieurs reprises en suivant le pointeur suivant, la liste aurait un cycle.
En règle générale, le dernier nœud de la liste liée fait référence à une référence nul pour indiquer la conclusion de la liste. Cependant, dans une liste liée à une boucle, le nœud final de la liste fait référence à un nœud de démarrage, un nœud interne, ou lui-même. Par conséquent, dans de telles circonstances, nous devons identifier et résilier la boucle en définissant la référence du nœud suivant à NULL. La partie de détection de la boucle dans une liste liée est expliquée dans cet article.
En C ++, il existe de nombreuses façons de trouver des boucles dans une liste liée:
Approche basée sur la table de hachage: Cette approche stocke les adresses des nœuds visités dans une table de hachage. Une boucle dans la liste liée existe si un nœud est déjà présent dans la table de hachage lors de sa visite à nouveau.
Approche cyclable de Floyd: L'algorithme «tortue et lièvre», communément appelé algorithme de recherche de cycle de Floyd: cette technique utilise deux pointeurs, l'un se déplaçant plus lentement que l'autre et l'autre se déplaçant plus rapidement rapidement. Le pointeur plus rapide dépasse finalement le pointeur plus lent s'il y a une boucle dans la liste liée, révélant l'existence de la boucle.
Méthode récursive: Cette méthode passe par la liste liée en s'appelant encore et encore. La liste liée contient une boucle si le nœud actuel a déjà été visité.
Approche basée sur la pile: Cette approche stocke les adresses des nœuds visités dans une pile. Une boucle dans la liste liée est présente si un nœud est déjà là dans la pile lors de sa visite à nouveau.
Expliquons chaque approche en détail pour comprendre le concept.
Approche 1: Approche de Hashset
L'utilisation de hachage est la méthode la plus simple. Ici, nous passons par la liste une par une tout en gardant une table de hachage avec les adresses de nœud. Par conséquent, une boucle existe si nous avons déjà traversé l'adresse suivante du nœud actuel qui est déjà contenu dans le tableau de hachage. Sinon, il n'y a pas de boucle dans la liste liée si nous rencontrons null (i.e., atteindre la fin de la liste liée).
Il sera assez facile de mettre en œuvre cette stratégie.
Tout en traversant la liste liée, nous utiliserons un non ordonné_hashmap et continuerons à y ajouter des nœuds.
Maintenant, une fois que nous avons rencontré un nœud qui est déjà montré sur la carte, nous saurons que nous sommes arrivés au début de la boucle.
En faisant cela, le nœud de boucle de fin au lieu de se référer au nœud initial de la boucle commence à pointer vers nul.
Le temps et la complexité du temps et de l'espace de la méthode de hachage sont O (n). Le lecteur doit toujours être conscient que la mise en œuvre de la datastructure de hachage intégrée dans le langage de programmation déterminera la complexité du temps totale en cas de collision de hachage.
Implémentation du programme C ++ pour la méthode ci-dessus (hashset):
#inclure
Utilisation de Namespace Std;
nœud struct
INT VALEUR;
nœud struct * suivant;
;
Nœud * newNode (INT Value)
Nœud * tempnode = nouveau nœud;
tempnode-> value = valeur;
tempnode-> next = null;
retour tempnode;
// identifier et éliminer toutes les boucles potentielles
// dans une liste liée à cette fonction.
void functionhashmap (nœud * headnode)
// a créé un non ordonné_map pour implémenter la carte de hachage
non ordonné_maphash_map;
// pointeur vers LastNode
Nœud * lastNode = null;
Pendant (Node de tête != Null)
// Si un nœud est absent sur la carte, ajoutez-le.
if (hash_map.find (headnode) == hash_map.fin())
hash_map [headnode] ++;
LastNode = headNode;
headnode = headnode-> suivant;
// Si un cycle est présent, définissez le prochain pointeur du nœud final vers NULL.
autre
LastNode-> next = null;
casser;
// Afficher la liste liée
VOID Affichage (Node * HEADNODE)
Pendant (Node de tête != Null)
couterheadnode = headnode-> suivant;
couter << endl;
/* Fonction principale*/
int main()
Node * 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);
/ * Créez une boucle dans LinkedList * /
headnode-> next-> next-> next-> next-> next = headnode-> next-> next;
functionhashmap (headnode);
printf ("Liste liée sans boucle \ n");
affichage (headnode);
retour 0;
Sortir:
Liste liée sans boucle
48 18 13 2 8
L'explication étape par étape du code est fournie ci-dessous:
Approche 2: le cycle de Floyd
L'algorithme de détection de cycle de Floyd, souvent connu sous le nom de Tortoise et l'algorithme de lièvre, fournit la base de cette méthode de localisation des cycles dans une liste liée. Le pointeur «lent» et le pointeur «rapide», qui traversent la liste à différentes vitesses, sont les deux pointeurs utilisés dans cette technique. Le pointeur rapide progresse deux étapes tandis que le pointeur lent fait avancer une étape à chaque itération. Un cycle dans la liste liée existe si les deux points se retrouvent face à face.
1. Avec le nœud de tête de la liste liée, nous initialisons deux pointeurs appelés Fast and Slow.
2. Maintenant, nous exécutons une boucle pour se déplacer dans la liste liée. Le pointeur rapide doit être déplacé deux emplacements devant le pointeur lent à chaque étape de chaque itération.
3. Il n'y aura pas de boucle dans la liste liée si le pointeur rapide atteint la fin de la liste (fastpointer == null ou fastpointer-> next == null). Sinon, les pointeurs rapides et lents finiront par se rencontrer, ce qui signifie que la liste liée a une boucle.
Implémentation du programme C ++ pour la méthode ci-dessus (cycle de Floyd):
#inclure
Utilisation de Namespace Std;
/ * LIEN LIST NODE * /
nœud struct
données int;
nœud struct * suivant;
;
/ * Fonction pour supprimer la boucle. * /
void deleteleloop (nœud struct *, nœud struct *);
/ * Cette fonction localise et élimine les boucles de liste. Il donne 1
S'il y avait une boucle dans la liste; Sinon, il renvoie 0. * /
inttectandDeleteleloop (liste de nœud structure *)
struct nœud * slowptr = list, * fastptr = list;
// Itérater pour vérifier si la boucle est là.
while (slowptr && fastptr && fastptr-> suivant)
SlowPtr = SlowPtr-> Suivant;
fastptr = fastptr-> suivant-> suivant;
/ * Si SlowPtr et FastPtr se rencontrent à un moment donné, alors là
est une boucle * /
if (slowptr == Fastptr)
DeleleTeloop (SlowPtr, List);
/ * Retour 1 pour indiquer qu'une boucle a été découverte. * /
retour 1;
/ * Retour 0 pour indiquer qu'aucune boucle n'a été découverte.* /
retour 0;
/ * Fonction pour supprimer la boucle de la liste liée.
Loopnode pointe vers l'un des nœuds de boucle et le nœud de tête pointe
au nœud de démarrage de la liste liée * /
void deleteleloop (nœud struct * loopnode, struct nœud * headnode)
nœud struct * ptr1 = loopnode;
nœud struct * ptr2 = loopnode;
// compter le nombre de nœuds dans la boucle.
non signé int k = 1, i;
tandis que (ptr1-> suivant != ptr2)
ptr1 = ptr1-> suivant;
k ++;
// Correction d'un pointeur vers la tête de tête
ptr1 = headnode;
// et l'autre pointeur vers k nœuds après Node de tête
ptr2 = headnode;
pour (i = 0; i < k; i++)
ptr2 = ptr2-> Suivant;
/ * Lorsque les deux points sont déplacés simultanément,
Ils colliteront au nœud débutant de la boucle. * /
pendant que (ptr2 != ptr1)
ptr1 = ptr1-> suivant;
ptr2 = ptr2-> Suivant;
// Obtenez le dernier pointeur du nœud.
tandis que (ptr2-> suivant != ptr1)
ptr2 = ptr2-> Suivant;
/ * Pour fermer la boucle, définissez le suivant
nœud au nœud de terminaison de la boucle. * /
ptr2-> next = null;
/ * Fonction pour afficher la liste liée * /
void DisplayLinkEdList (nœud struct * nœud)
// Affiche la liste liée après la suppression de la boucle
while (nœud != Null)
couter Node = Node-> Suivant;
nœud struct * newNode (key int)
struct nœud * temp = new node ();
temp-> data = key;
temp-> next = null;
Tempère de retour;
// Code principal
int main()
struct nœud * 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);
/ * Créer une boucle * /
headnode-> next-> next-> next-> next-> next = headnode-> next-> next;
// Affiche la boucle dans la liste liée
// DisplayLinkEdList (HeadNode);
DetectandDeleteleop (HeadNode);
couter << "Linked List after no loop \n";
DisplayLinkedList (HeadNode);
retour 0;
Sortir:
Liste liée après aucune boucle
48 18 13 2 8
Explication:
Approche 3: Recursion
La récursivité est une technique pour résoudre les problèmes en les séparant en sous-problèmes plus petits et plus faciles. La récursivité peut être utilisée pour parcourir une liste liée individuellement dans le cas où une boucle est trouvée en exécutant en continu une fonction pour le nœud suivant dans la liste jusqu'à ce que la fin de la liste soit atteinte.
Dans une liste liée individuellement, le principe de base derrière l'utilisation de la récursivité pour trouver une boucle est de commencer à la tête de la liste, de marquer le nœud actuel comme visité à chaque étape, puis de passer au nœud suivant en invoquant récursivement la fonction de Ce nœud. La méthode itérera sur la liste liée complète car elle s'appelle récursivement.
La liste contient une boucle si un nœud qui était auparavant marqué comme visité est rencontré par la fonction; Dans ce cas, la fonction peut retourner vrai. La méthode peut retourner false si elle atteint la fin de la liste sans courir sur un nœud visité, ce qui indique qu'il n'y a pas de boucle.
Bien que cette technique d'utilisation de la récursivité de trouver une boucle dans une seule liste liée soit simple à utiliser et à comprendre, elle pourrait ne pas être la plus efficace en termes de complexité de temps et d'espace.
Implémentation du programme C ++ pour la méthode ci-dessus (Recursion):
#inclure
Utilisation de Namespace Std;
nœud struct
données int;
Nœud * suivant;
Bool visité;
;
// Fonction de détection de boucle de liste liée
bool DetectloopLinkEdList (node * headnode)
if (headnode == null)
retourne false; // Si la liste liée est vide, le cas de base
// il y a une boucle si le nœud actuel a
// déjà visité.
if (headnode-> visité)
Retour Vrai;
// Ajouter une marque de visite au nœud actuel.
headnode-> visité = true;
// appelle le code pour le nœud suivant à plusieurs reprises
return DetectLoOpLinkEdList (headNode-> Suivant);
int main()
Node * headnode = new node ();
Node * secondNode = new node ();
Node * tiersnode = new node ();
headnode-> data = 1;
headnode-> next = secondNode;
headnode-> visité = false;
secondNode-> data = 2;
secondNode-> next = tiersnode;
secondNode-> visité = false;
ThirdNode-> data = 3;
ThirdNode-> next = null; // pas de boucle
ThirdNode-> Visited = false;
if (DetectLoopLinkEdList (headnode))
couter << "Loop detected in linked list" << endl;
autre
couter << "No loop detected in linked list" << endl;
// Création d'une boucle
tiersNode-> next = secondNode;
if (DetectLoopLinkEdList (headnode))
couter << "Loop detected in linked list" << endl;
autre
couter << "No loop detected in linked list" << endl;
retour 0;
Sortir:
Aucune boucle détectée dans la liste liée
Boucle détectée dans la liste liée
Explication:
Approche 4: Utilisation de pile
Des boucles dans une liste liée peuvent être trouvées à l'aide d'une pile et de la méthode de la «recherche en profondeur-première» (DFS). Le concept fondamental est d'itérer à travers la liste liée, poussant chaque nœud sur la pile s'il n'a pas déjà été visité. Une boucle est reconnue si un nœud qui se trouve déjà sur la pile se retrouve à nouveau.
Voici une brève description de la procédure:
Implémentation du programme C ++ pour la méthode ci-dessus (pile)
#inclure
#inclure
Utilisation de Namespace Std;
nœud struct
données int;
Nœud * suivant;
;
// fonction à détecter la boucle dans une liste liée
bool DetectloopLinkEdList (node * headnode)
empilerempiler;
Node * tempnode = headnode;
pendant (tempnode != Null)
// Si l'élément supérieur de la pile correspond
// le nœud actuel et la pile n'est pas vide
si (!empiler.vide () && pile.top () == tempnode)
Retour Vrai;
empiler.push (tempnode);
tempnode = tempnode-> suivant;
retourne false;
int main()
Node * headnode = new node ();
Node * secondNode = new node ();
Node * tiersnode = new node ();
headnode-> data = 1;
headnode-> next = secondNode;
secondNode-> data = 2;
secondNode-> next = tiersnode;
ThirdNode-> data = 3;
ThirdNode-> next = null; // pas de boucle
if (DetectLoopLinkEdList (headnode))
couter << "Loop detected in linked list" << endl;
autre
couter << "No loop detected in linked list" << endl;
// Création d'une boucle
tiersNode-> next = secondNode;
if (DetectLoopLinkEdList (headnode))
couter << "Loop detected in linked list" << endl;
autre
couter << "No loop detected in linked list" << endl;
Sortir:
Aucune boucle détectée dans la liste liée
Boucle détectée dans la liste liée
Explication:
Ce programme utilise une pile pour savoir si une liste liée individuelle a une boucle.
2. L'espace de noms standard est inclus dans la deuxième ligne, ce qui nous permet d'accéder aux fonctions de la bibliothèque de flux d'entrée / sortie sans avoir à les préfixer avec «std ::."
3. La ligne suivante définit le nœud struct, qui se compose de deux membres: un entier appelé «données» et un pointeur vers un autre nœud appelé «Next."
4. La tête de la liste liée est une entrée pour la méthode DetectloopLinkEdList (), qui est définie sur la ligne suivante. La fonction produit une valeur booléenne qui indique si une boucle a été trouvée ou non.
5. Une pile de pointeurs de nœuds appelés «pile» et un pointeur vers un nœud nommé «tempnode» qui est initialisé avec la valeur du nœud de tête est tous deux créés à l'intérieur de la fonction.
6. Ensuite, tant que Tempnode n'est pas un pointeur nul, nous entrons dans une boucle de temps.
(a) L'élément supérieur de la pile doit correspondre au nœud actuel afin que nous puissions déterminer qu'il n'est pas vide. Nous revenons vrai si tel est le cas parce qu'une boucle a été trouvée.
(b) Si la condition susmentionnée est fausse, le pointeur de nœud actuel est poussé sur la pile et Tempnode est défini sur le nœud suivant dans la liste liée.
7. Nous retournons faux après la boucle while car aucune boucle n'a été observée.
8. Nous construisons trois objets de nœud et les initialisons dans la fonction principale (). Puisqu'il n'y a pas de boucle dans le premier exemple, nous définissons correctement les «prochains» pointeurs de chaque nœud.
Conclusion:
En conclusion, la meilleure méthode pour détecter les boucles dans une liste liée dépend du cas d'utilisation spécifique et des contraintes du problème. La table de hachage et l'algorithme de recherche de cycle de Floyd sont des méthodes efficaces et ils sont largement utilisés dans la pratique. La pile et la récursivité sont des méthodes moins efficaces mais elles sont plus intuitives.