Mejor algoritmo para probar si una lista enlazada tiene un ciclo


¿Cuál es el mejor algoritmo (detención) para determinar si una lista enlazada tiene un ciclo en ella?

[Editar] El análisis de la complejidad asintótica tanto para el tiempo como para el espacio sería dulce para que las respuestas se puedan comparar mejor.

[Editar] La pregunta original no se refería a nodos con outdegree > 1, pero se habla de ello. Esa pregunta está más en la línea de "Mejor algoritmo para detectar ciclos en un gráfico dirigido".

Author: eeerahul, 2008-08-29

13 answers

Tener dos punteros iterando a través de la lista; hacer una iteración a través del doble de la velocidad del otro, y comparar sus posiciones en cada paso. De la parte superior de mi cabeza, algo como:

node* tortoise(begin), * hare(begin);
while(hare = hare->next)
{
    if(hare == tortoise) { throw std::logic_error("There's a cycle"); }
    hare = hare->next;
    if(hare == tortoise) { throw std::logic_error("There's a cycle"); }
    tortoise = tortoise->next;
}

O(n), que es lo mejor que puedes conseguir.

 49
Author: DrPizza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-03 06:04:58

Condición previa: Mantenga un registro del tamaño de la lista (actualice el tamaño cuando se agregue o elimine un nodo).

Detección de bucle:

  1. Mantenga un contador cuando atraviese el tamaño de la lista.

  2. Si el contador excede el tamaño de la lista, hay un ciclo posible.

Complejidad: O(n)

Nota: la comparación entre el contador y el tamaño de la lista, así como la operación de actualización del tamaño de la lista, deben ser seguras para el hilo.

 1
Author: kean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-16 22:18:58

Tome 2 punteros * p y * q, comience a recorrer la lista enlazada "LL" usando ambos punteros:

1) el puntero p eliminará el nodo anterior cada vez y apuntando al siguiente nodo.

2) el puntero q irá cada vez solo en dirección hacia adelante.

Condiciones:

1) el puntero p está apuntando a null y q está apuntando a algún nodo : El bucle está presente

2) ambos puntero apuntando a null: no hay bucle

 1
Author: mayank joshi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-24 10:17:57

¿Qué hay de usar una tabla hash para almacenar los nodos ya vistos (los observa en orden desde el comienzo de la lista)? En la práctica, se podría lograr algo cercano a O (N).

De lo contrario, usando un montón ordenado en lugar de una tabla hash se lograría O(N log(N)).

 0
Author: OysterD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-29 09:33:50

Me pregunto si hay alguna otra manera que simplemente ir iterativamente - rellenar una matriz a medida que avanza, y comprobar si el nodo actual ya está presente en la matriz...

 0
Author: Henrik Paul,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-29 09:34:45

El algoritmo de Konrad Rudolph no funcionará si el ciclo no apunta al principio. La siguiente lista lo convertirá en un bucle infinito: 1->2->3->2.

El algoritmo de DrPizza es definitivamente el camino a seguir.

 0
Author: Liedman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-29 09:46:24

En este caso el código de OysterD será la solución más rápida (coloración de vértices)

Eso realmente me sorprendería. Mi solución hace como máximo dos pasadas a través de la lista (si el último nodo está vinculado a la penúltima lode), y en el caso común (sin bucle) hará solo una pasada. Sin hash, sin asignación de memoria, etc.

 0
Author: DrPizza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-29 09:52:44

En este caso el código de OysterD será la solución más rápida (coloración de vértices)

Eso realmente me sorprendería. Mi solución hace como máximo dos pasadas a través de la lista (si el último nodo está vinculado a la penúltima lode), y en el caso común (sin bucle) hará solo una pasada. Sin hash, sin asignación de memoria, etc.

Sí. He notado que la formulación no era perfecta y la he reformulado. Todavía creo que un hachís inteligente podría funcionar más rápido-por un pelo. Creo que su algoritmo es la mejor solución, sin embargo.

Solo para subrayar mi punto: la coloración vertec se usa para detectar ciclos en dependencias por los recolectores de basura modernos, por lo que hay un caso de uso muy real para ello. En su mayoría utilizan banderas de bits para realizar la coloración.

 0
Author: Konrad Rudolph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-29 09:58:35

Tendrá que visitar cada nodo para determinar esto. Esto se puede hacer recursivamente. Para evitar que visites nodos ya visitados, necesitas un indicador que diga 'ya visitado'. Esto, por supuesto, no te da bucles. Así que en lugar de una bandera de bits, utilice un número. Empieza a la 1. Compruebe los nodos conectados y luego márquelos como 2 y recurra hasta que la red esté cubierta. Si al comprobar nodos se encuentra con un nodo que es más de uno menos que el nodo actual, entonces tiene un ciclo. La duración del ciclo está dada por la diferencia.

 0
Author: Guillermo Phillips,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-26 15:05:15

Dos punteros se inicializan al principio de la lista. Un puntero avanza una vez en cada paso, y el otro avanza dos veces en cada paso. Si el puntero más rápido se encuentra con el más lento de nuevo, hay un bucle en la lista. De lo contrario no hay bucle si el más rápido llega al final de la lista.

El siguiente código de ejemplo se implementa de acuerdo con esta solución. El puntero más rápido es pFast, y el más lento es pSlow.

bool HasLoop(ListNode* pHead)
{
    if(pHead == NULL)
        return false;


    ListNode* pSlow = pHead->m_pNext;
    if(pSlow == NULL)
        return false;


    ListNode* pFast = pSlow->m_pNext;
    while(pFast != NULL && pSlow != NULL)
    {
        if(pFast == pSlow)
            return true;


        pSlow = pSlow->m_pNext;


        pFast = pFast->m_pNext;
        if(pFast != NULL)
            pFast = pFast->m_pNext;
    }


    return false;
}

Esta solución está disponible en mi blog. Hay un problema más discutido en el blog: ¿ Cuál es el nodo de entrada cuando hay ciclo / bucle en una lista?

 0
Author: Harry He,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-28 13:41:04

Solución "Hack" (debería funcionar en C/C++):

  • Recorre la lista y establece el último bit del puntero next en 1.
  • If find an element with flagged pointer return return true y el primer elemento del ciclo.
  • Antes de regresar, restablezca los punteros, aunque creo que la desreferenciación funcionará incluso con punteros marcados.

La complejidad temporal es 2n. Parece que no utiliza ninguna variable temporal.

 0
Author: Ayrat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-31 08:02:21

Esta es una solución que utiliza una tabla Hash ( solo una lista en realidad) para guardar la dirección del puntero.

def hash_cycle(node):
    hashl=[]
    while(node):
        if node in hashl:
            return True
        else:
            hashl.append(node)
            node=node.next
    return False
 0
Author: FlyingAura,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-19 08:16:32

Def has_cycle (cabeza): counter = set ()

while head is not None:
    if head in counter:
        return True
    else:
        counter.add(head)
        head = head.next
 0
Author: ericgu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-16 14:54:41