El nodo final de una lista vinculada que tiene un bucle se referirá a otro nodo en la misma lista en lugar del nulo.Si hay un nodo en una lista vinculada a la que se puede acceder repetidamente siguiendo el siguiente puntero, se dice que la lista tiene un ciclo.
Por lo general, el último nodo de la lista vinculada se refiere a una referencia nula para denotar la conclusión de la lista. Sin embargo, en una lista vinculada con un bucle, el nodo final de la lista se refiere a un nodo de inicio, un nodo interno o sí mismo. Por lo tanto, en tales circunstancias, debemos identificar y terminar el bucle estableciendo la referencia del siguiente nodo a NULL. La parte de detección del bucle en una lista vinculada se explica en este artículo.
En C ++, hay numerosas formas de encontrar bucles en una lista vinculada:
Enfoque basado en la mesa hash: Este enfoque almacena las direcciones de los nodos visitados en una tabla hash. Existe un bucle en la lista vinculada si un nodo ya está presente en la tabla hash cuando se vuelve a visitar.
Enfoque del ciclo de Floyd: El algoritmo "Tortoise and Hare", comúnmente conocido como algoritmo de búsqueda de ciclo de Floyd: esta técnica utiliza dos punteros, uno que se mueve más lentamente que el otro y el otro que se mueve más rápidamente. El puntero más rápido superará en última instancia el puntero más lento si hay un bucle en la lista vinculada, revelando la existencia del bucle.
Método recursivo: Este método pasa por la lista vinculada llamándose una y otra vez. La lista vinculada contiene un bucle si el nodo actual se ha visitado previamente.
Enfoque basado en pila: Este enfoque almacena las direcciones de los nodos visitados en una pila. Un bucle en la lista vinculada está presente si un nodo ya está allí en la pila cuando se vuelve a visitar.
Expliquemos cada enfoque en detalle para comprender el concepto.
Enfoque 1: enfoque hashset
Utilizar el hashing es el método más directo. Aquí, revisamos la lista uno por uno mientras mantenemos una tabla hash con las direcciones de nodo. Por lo tanto, existe un bucle si alguna vez nos encontramos con la siguiente dirección del nodo actual que ya está contenido en la tabla hash. De lo contrario, no hay bucle en la lista vinculada si nos encontramos con nulo (i.mi., llegar al final de la lista vinculada).
Será bastante fácil implementar esta estrategia.
Mientras atraviesa la lista vinculada, utilizaremos un desordenado_hashmap y seguiremos agregando nodos.
Ahora, una vez que nos encontremos con un nodo que ya se muestra en el mapa, sabremos que hemos llegado al comienzo del bucle.
Al hacer esto, el nodo de bucle final en lugar de referirse al nodo inicial del bucle comienza a señalar a NULL.
El tiempo promedio y la complejidad del espacio del método hash es o (n). El lector siempre debe ser consciente de que la implementación de la datos de hashing incorporada en el lenguaje de programación determinará la complejidad del tiempo total en caso de una colisión en el hashing.
Implementación del programa C ++ para el método anterior (hashset):
#incluir
usando el espacio de nombres STD;
nodo de estructura
valor int;
nodo de estructura* Siguiente;
;
Nodo* Newnode (int value)
Nodo* tempNode = nuevo nodo;
tempnode-> valor = valor;
tempnode-> next = null;
return tempnode;
// identificar y eliminar cualquier bucle potencial
// en una lista vinculada con esta función.
FunctionHashMap (nodo* nodo)
// Creó un MAPS de UNDERSED_MAP para implementar el mapa hash
desordenable_maphash_map;
// puntero a lastNode
Nodo* LastNode = null;
mientras (nodo != Nulo)
// Si falta un nodo en el mapa, agrégalo.
if (hash_map.find (headNode) == hash_map.fin())
hash_map [headNode] ++;
dastNode = headNode;
headNode = headNode-> next;
// Si hay un ciclo presente, establezca el siguiente puntero del nodo final en NULL.
demás
dastNode-> next = null;
romper;
// Muestra la lista vinculada
pantalla vacía (nodo* nodo)
mientras (nodo != Nulo)
coutheadNode = headNode-> next;
cout << endl;
/* Función principal*/
int main ()
Nodo* 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);
/ * Crear un bucle en LinkedList */
headNode-> next-> next-> next-> next-> next = headNode-> next-> next;
functionHashMap (HeadNode);
printf ("Lista vinculada sin bucle \ n");
visualización (headNode);
regresar 0;
Producción:
Lista vinculada sin bucle
48 18 13 2 8
La explicación paso a paso del código se proporciona a continuación:
Enfoque 2: ciclo de Floyd
El algoritmo de detección de ciclo de Floyd, a menudo conocido como la tortuga y el algoritmo Hare, proporciona la base para este método de localización de ciclos en una lista vinculada. El puntero "lento" y el puntero "rápido", que atraviesa la lista a varias velocidades, son los dos punteros utilizados en esta técnica. El puntero rápido avanza dos pasos, mientras que el puntero lento avanza un paso cada iteración. Existe un ciclo en la lista vinculada si los dos puntos se enfrentan cara a cara.
1. Con el nodo principal de la lista vinculada, inicializamos dos punteros llamados Fast and Slow.
2. Ahora ejecutamos un bucle para movernos a través de la lista vinculada. El puntero rápido debe moverse dos ubicaciones frente al puntero lento en el paso de cada iteración.
3. No habrá un bucle en la lista vinculada si el puntero rápido alcanza el final de la lista (fastPointer == null o fastpointer-> next == null). Si no, los punteros rápidos y lentos eventualmente se reunirán, lo que significa que la lista vinculada tiene un bucle.
Implementación del programa C ++ para el método anterior (ciclo de Floyd):
#incluir
usando el espacio de nombres STD;
/ * Nodo de lista de enlaces */
nodo de estructura
datos int;
nodo de estructura* Siguiente;
;
/* Función para eliminar el bucle. */
void deleteloop (nodo de struct*, nodo struct*);
/* Esta función localiza y elimina los bucles de la lista. Rinde 1
Si había un bucle en la lista; más, devuelve 0. */
int
struct node *lentoPtr = list, *fastPtr = list;
// iterar para verificar si el bucle está ahí.
while (slowptr && fastptr && fastptr-> next)
SlowPtr = SlowPtr-> Siguiente;
fastPtr = fastPtr-> next-> next;
/* Si SlowPtr y FastPtr se encuentran en algún momento, entonces allí
es un bucle */
if (slowptr == fastPtr)
deleteloop (slowptr, lista);
/* Regresar 1 para indicar que se ha descubierto un bucle. */
regresar 1;
/* Devolver 0 para indicar que no se ha descubierto ningún bucle.*/
regresar 0;
/* Función para eliminar el bucle de la lista vinculada.
Loopnode apunta a uno de los nodos de bucle y el noto del encendido está apuntando
al nodo de inicio de la lista vinculada */
Void DeletELOOP (Struct Node* Loopnode, Nodode Struct* HeadNode)
struct nodo* ptr1 = loopnode;
struct nodo* ptr2 = loopnode;
// Cuenta cuántos nodos hay en el bucle.
Unsigned int k = 1, i;
Mientras (Ptr1-> Siguiente != ptr2)
ptr1 = ptr1-> next;
K ++;
// arregla un puntero al nodo de cabeza
ptr1 = headNode;
// y el otro puntero a k nodos después del nodo de cabeza
ptr2 = headNode;
para (i = 0; i < k; i++)
ptr2 = ptr2-> next;
/* Cuando ambos puntos se mueven simultáneamente,
chocarán en el nodo inicial del bucle. */
Mientras (PTR2 != ptr1)
ptr1 = ptr1-> next;
ptr2 = ptr2-> next;
// Obtener el último puntero del nodo.
mientras (Ptr2-> Siguiente != Ptr1)
ptr2 = ptr2-> next;
/* Para cerrar el bucle, establezca el posterior
nodo al nodo de terminación del bucle. */
ptr2-> next = null;
/ * Función para mostrar la lista vinculada */
Void DisplayLinkedList (nodo de nodo* struct)
// Muestra la lista vinculada después de eliminar el bucle
mientras (nodo != Nulo)
cout nodo = nodo-> next;
struct node* newnode (int key)
struct node* temp = new Node ();
temp-> data = key;
temp-> next = null;
regresar temp;
// Código principal
int main ()
struct node* 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);
/ * Crear un bucle */
headNode-> next-> next-> next-> next-> next = headNode-> next-> next;
// Muestra el bucle en la lista vinculada
// DisplayLinkedList (HeadNode);
detectanddeletelop (nodo de cabeza);
cout << "Linked List after no loop \n";
DisplayLinkedList (HeadNode);
regresar 0;
Producción:
Lista vinculada después de ningún bucle
48 18 13 2 8
Explicación:
Enfoque 3: Recursión
La recursión es una técnica para resolver problemas al dividirlos en subproblemas más pequeños y más fáciles. La recursión se puede utilizar para atravesar una lista vinculada individualmente en caso de que se encuentre un bucle ejecutando continuamente una función para el siguiente nodo en la lista hasta que se alcance el final de la lista.
En una lista vinculada individualmente, el principio básico detrás del uso de la recursión para encontrar un bucle es comenzar en el cabezal de la lista, marcar el nodo actual como se ve en cada paso y luego continuar al siguiente nodo invocando recursivamente la función para ese nodo. El método se iterará en la lista vinculada completa porque se llama recursivamente.
La lista contiene un bucle si la función se encuentra un nodo que se ha marcado previamente según lo visitado; En este caso, la función puede devolver verdad. El método puede devolver falso si llega al final de la lista sin ejecutar un nodo visitado, lo que indica que no hay bucle.
Aunque esta técnica para utilizar la recursión para encontrar un bucle en una sola lista vinculada es sencilla de usar y comprender, podría no ser el más efectivo en términos de tiempo y complejidad espacial.
Implementación del programa C ++ para el método anterior (recursión):
#incluir
usando el espacio de nombres STD;
nodo de estructura
datos int;
Nodo* siguiente;
Bool visitó;
;
// función de detección de bucle de lista vinculada
bool DetectloopLinkedList (nodo* headNode)
if (headNode == null)
falso retorno; // Si la lista vinculada está vacía, el caso básico
// Hay un bucle si el nodo actual tiene
// ya ha sido visitado.
if (headNode-> visitado)
devolver verdadero;
// Agregar una marca de visitas al nodo actual.
HeadNode-> Visited = True;
// llamando al código para el nodo posterior repetidamente
return DetectLoopLinkEdList (HeadNode-> Next);
int main ()
Nodo* headNode = new Node ();
Nodo* SecondNode = new Node ();
Nodo* tercero = nuevo nodo ();
HeadNode-> data = 1;
HeadNode-> next = SecondNode;
HeadNode-> Visited = false;
SecondNode-> data = 2;
SecondNode-> next = ThirdNode;
SecondNode-> Visited = false;
ThirdNode-> data = 3;
ThirdNode-> next = null; // sin bucle
ThirdNode-> Visited = false;
if (DetectLoopLinkedList (HeadNode))
cout << "Loop detected in linked list" << endl;
demás
cout << "No loop detected in linked list" << endl;
// Creando un bucle
ThirdNode-> next = SecondNode;
if (DetectLoopLinkedList (HeadNode))
cout << "Loop detected in linked list" << endl;
demás
cout << "No loop detected in linked list" << endl;
regresar 0;
Producción:
Sin bucle detectado en la lista vinculada
Bucle detectado en la lista vinculada
Explicación:
Enfoque 4: Uso de la pila
Los bucles en una lista vinculada se pueden encontrar utilizando una pila y el método "Búsqueda de profundidad" (DFS). El concepto fundamental es iterar a través de la lista vinculada, empujando cada nodo a la pila si aún no se ha visitado. Se reconoce un bucle si se encuentra un nodo que ya está en la pila.
Aquí hay una breve descripción del procedimiento:
Implementación del programa C ++ para el método anterior (pila)
#incluir
#incluir
usando el espacio de nombres STD;
nodo de estructura
datos int;
Nodo* siguiente;
;
// Función para detectar el bucle en una lista vinculada
bool DetectloopLinkedList (nodo* headNode)
pilapila;
Nodo* tempnode = headNode;
mientras (tempnode != Nulo)
// Si el elemento superior de la pila coincide
// El nodo actual y la pila no está vacía
si (!pila.stack vacía () &&.top () == tempnode)
devolver verdadero;
pila.push (tempnode);
tempnode = tempnode-> next;
falso retorno;
int main ()
Nodo* headNode = new Node ();
Nodo* SecondNode = new Node ();
Nodo* tercero = nuevo nodo ();
HeadNode-> data = 1;
HeadNode-> next = SecondNode;
SecondNode-> data = 2;
SecondNode-> next = ThirdNode;
ThirdNode-> data = 3;
ThirdNode-> next = null; // sin bucle
if (DetectLoopLinkedList (HeadNode))
cout << "Loop detected in linked list" << endl;
demás
cout << "No loop detected in linked list" << endl;
// Creando un bucle
ThirdNode-> next = SecondNode;
if (DetectLoopLinkedList (HeadNode))
cout << "Loop detected in linked list" << endl;
demás
cout << "No loop detected in linked list" << endl;
Producción:
Sin bucle detectado en la lista vinculada
Bucle detectado en la lista vinculada
Explicación:
Este programa usa una pila para averiguar si una lista vinculada individualmente tiene un bucle.
2. El espacio de nombres estándar se incluye en la segunda línea, lo que nos permite acceder a las funciones de la biblioteca de transmisión de entrada/salida sin tener que prefijarlas con "STD ::."
3. La siguiente línea define el nodo de estructura, que consta de dos miembros: un entero llamado "datos" y un puntero a otro nodo llamado "Siguiente."
4. El cabezal de la lista vinculada es una entrada para el método DetectloopLinkedList (), que se define en la siguiente línea. La función produce un valor booleano que indica si se encontró o no un bucle.
5. Una pila de punteros de nodo llamado "pila" y un puntero a un nodo llamado "tempnode" que se inicializa con el valor del nodo se crea dentro de la función.
6. Entonces, siempre que Tempnode no sea un puntero nulo, entramos en un bucle de tiempo.
(a) El elemento superior de la pila debe coincidir con el nodo actual para que podamos determinar que no está vacío. Devuelve verdadero si este es el caso porque se ha encontrado un bucle.
(b) Si la condición mencionada anteriormente es falsa, el puntero del nodo actual se empuja a la pila, y Tempnode se establece en el siguiente nodo en la lista vinculada.
7. Devuelve falso después del bucle de ruiho porque no se observó un bucle.
8. Construimos tres objetos de nodo y los inicializamos en la función Main (). Dado que no hay bucle en el primer ejemplo, establecemos los "Siguientes" punteros de cada nodo correctamente.
Conclusión:
En conclusión, el mejor método para detectar bucles en una lista vinculada depende del caso de uso específico y las restricciones del problema. La tabla hash y el algoritmo de búsqueda de ciclo de Floyd son métodos eficientes y se usan ampliamente en la práctica. La pila y la recursión son métodos menos eficientes, pero son más intuitivos.