web-dev-qa-db-fra.com

Quel est un algorithme efficace pour déterminer si une liste à lien unique est circulaire/cyclique ou non?

Comment puis-je savoir si une liste à lien unique est circulaire/cyclique ou non? J'ai essayé de chercher mais je n'ai pas trouvé de solution satisfaisante. Si possible, pouvez-vous fournir un pseudo-code ou une implémentation Java?

Par exemple:
135714575, où le second 5 est en fait le troisième élément de la liste.

36
harshit

La réponse standard consiste à prendre deux itérateurs au début, à incrémenter le premier une fois et le second deux fois. Vérifiez pour voir s'ils pointent vers le même objet. Répétez ensuite jusqu'à ce que celui qui incrémente deux fois soit frappe le premier ou atteint la fin.

Cet algorithme trouve un lien circulaire dans la liste, pas seulement un cercle complet.

Pseudo-code (pas en Java, non testé - ça m'est arrivé)

bool hasCircle(List l)
{
   Iterator i = l.begin(), j = l.begin();
   while (true) {
      // increment the iterators, if either is at the end, you're done, no circle
      if (i.hasNext())  i = i.next(); else return false;

      // second iterator is travelling twice as fast as first
      if (j.hasNext())  j = j.next(); else return false;
      if (j.hasNext())  j = j.next(); else return false;

      // this should be whatever test shows that the two
      // iterators are pointing at the same place
      if (i.getObject() == j.getObject()) { 
          return true;
      } 
   }
}
76
Lou Franco

Un algorithme simple appelé algorithme de Floyd consiste à avoir deux pointeurs, a et b, qui commencent tous deux par le premier élément de la liste liée. Ensuite, à chaque étape, vous incrémentez une fois et b deux fois. Répétez l'opération jusqu'à atteindre la fin de la liste (pas de boucle) ou a == b (la liste liée contient une boucle).

Un autre algorithme est l'algorithme de Brent .

14
Mark Byers

Trois stratégies principales que je connais:

  1. Commencez par parcourir la liste et gardez une trace de tous les nœuds visités (enregistrez leurs adresses dans une carte, par exemple). Chaque nouveau nœud que vous visitez, vérifiez si vous l’avez déjà visité. Si vous avez déjà visité le nœud, il y a évidemment une boucle. S'il n'y a pas de boucle, vous finirez par atteindre la fin. Ce n’est pas formidable car O(N) complexité d'espace pour stocker les informations supplémentaires.

  2. La solution tortue/lièvre. Commencez deux pointeurs au début de la liste. Le premier pointeur, la "Tortue", avance d'un nœud à chaque itération. L'autre pointeur, le "lièvre" avance de deux nœuds à chaque itération. S'il n'y a pas de boucle, le lièvre et la tortue atteindront tous deux la fin de la liste. S'il y a une boucle, le lièvre passera la tortue à un moment donné et lorsque cela se produit, vous savez qu'il y a une boucle. Ceci est O(1) complexité spatiale et un algorithme assez simple.

  3. Utilisez l'algorithme pour inverser une liste liée. Si la liste est en boucle, vous revenez au début de la liste tout en essayant de l'inverser. S'il n'a pas de boucle, vous finissez de l'inverser et touchez la fin. Ceci est O(1) complexité spatiale, mais un algorithme légèrement plus laid.

8
Brent Writes Code

Si vous comptez vos nœuds et revenez à la tête.

4
Batman

Utilisez l’algorithme Tortoise-Hare .

2
leppie

Que diriez-vous de l'approche suivante:

Triez la liste de liens par ordre croissant en suivant les algorithmes standard. Avant de trier: 4-2-6-1-5 Après de trier: 1-2-4-5-6

Une fois triés, vérifiez les données de chaque nœud et comparez-les avec les données du nœud de liaison, comme suit:

if (currentcode-> data> currentnode-> link-> data) i.e. circular = true;

A toute comparaison, si l'un des "currentnode-> data" est supérieur à "currentcode-> link-> data" pour une liste de liens triée, cela signifie que le nœud actuel est pointé sur un nœud précédent (c'est-à-dire circulaire);

Les gars, je n'ai pas la configuration nécessaire pour tester le code. Laissez-moi maintenant si ce concept fonctionne.

2
newbie

Un algorithme est:

  1. Stocke le pointeur sur le premier noeud
  2. Parcourez la liste en comparant chaque pointeur de noeud à ce pointeur
  3. Si vous rencontrez un pointeur NULL, sa liste ne sera pas liée circulairement
  4. Si vous rencontrez le premier nœud lors de la traversée, sa liste est liée de manière circulaire.
1
Asha

Voici un site sympa sur lequel les différentes solutions peuvent être copiées.

trouver la liste des liens liés en boucle

C'est le gagnant sur ce site

// Best solution
function boolean hasLoop(Node startNode){
  Node slowNode = Node fastNode1 = Node fastNode2 = startNode;
  while (slowNode && fastNode1 = fastNode2.next() && fastNode2 = fastNode1.next()){
    if (slowNode == fastNode1 || slowNode == fastNode2) return true;
    slowNode = slowNode.next();
  }
  return false;
}

Cette solution est "l'algorithme de recherche de cycle de De Floyd" tel que publié Dans "Algorithmes non déterministes" par Robert W. Floyd en 1967. Il s'agit également de [. appelé "La Tortue et le Lièvre Algorithme".

0
Markus Lausberg

Commencez par un nœud et enregistrez-le, puis parcourez toute la liste jusqu'à atteindre un pointeur nul ou le nœud avec lequel vous avez commencé.

Quelque chose comme:

Node start = list->head;
Node temp = start->next;
bool circular = false;
while(temp != null && temp != start)
{
   if(temp == start)
   {
     circular = true;
     break;
   }
   temp = temp->next;
}
return circular

C’est O (n), ce qui est à peu près le meilleur que vous puissiez obtenir avec une liste à lien unique (corrigez-moi si je me trompe).

Ou pour trouver des cycles dans la liste (comme le milieu), vous pouvez faire:

Node[] array; // Use a vector or ArrayList to support dynamic insertions
Node temp = list->head;
bool circular = false;
while(temp != null)
{
   if(array.contains(temp) == true)
   {
     circular = true;
     break;
   }
   array.insert(temp);
   temp = temp->next;
}
return circular

Cela sera un peu plus lent en raison des temps d'insertion des tableaux dynamiques.

0
samoz

Essaye ça 

/* Link list Node */

struct Node { int data; struct Node * next; };

/ * Cette fonction renvoie true si la liste Associée est circulaire, sinon false. */ bool isCircular (struct Node * head) { // Une liste de liens vide est circulaire if (head == NULL) retourne vrai;

// Next of head
struct Node *node = head->next;

// This loop would stope in both cases (1) If
// Circular (2) Not circular
while (node != NULL && node != head)
   node = node->next;

// If loop stopped because of circular
// condition
return (node == head);

}

0
Dwivedi Ji

@ samoz a à mon avis la réponse! Pseudo-code manquant. Serait quelque chose comme

votre liste est votre liste chaînée

allnodes = hashmap
while yourlist.hasNext()
   node = yourlist.next()
   if(allnodes.contains(node))
      syso "loop found"
      break;
   hashmap.add(node)

désolé, le code est très pseudo (faire plus de script que Java dernièrement)

0
leo

Cela ne se terminera jamais à partir de la boucle, cela peut aussi être fait avec la solution suivante:

bool hasCircle(List l)
{
   Iterator i = l.begin(), j = l.begin();
   while (true) {
      // increment the iterators, if either is at the end, you're done, no circle
      if (i.hasNext())  i = i.next(); else return false;

      // second iterator is travelling twice as fast as first
      if (j.hasNext())  j = j.next(); else return false;
      if (j.hasNext())  j = j.next(); else return false;

      // this should be whatever test shows that the two
      // iterators are pointing at the same place
      if (i.getObject() == j.getObject()) { 
          return true;
      } 
      if(i.next()==j)
          break;
    }
}
0
ajay