Lista enlazada circular de Java (explicación detallada de imágenes y texto)

Tabla de contenido

1. Determinar si hay un ciclo en la lista vinculada.

(1) Descripción del título

(2) Solución

2. El punto de entrada de la lista circular enlazada.

(1) Descripción del título

(2) Solución


1. Determinar si hay un ciclo en la lista vinculada.

(1) Descripción del título

Dado el nodo principal de una lista vinculada  head , determine si hay un ciclo en la lista vinculada.

Si hay un nodo en la lista vinculada al que se puede acceder nuevamente siguiendo continuamente el siguiente puntero, entonces hay un ciclo en la lista vinculada. Para representar un anillo en una lista vinculada determinada, el sistema de evaluación utiliza internamente un número entero  pos para representar la posición en la lista vinculada donde está conectada la cola de la lista vinculada (índice que comienza desde 0). Nota: pos No se pasa como parámetro  . Solo para identificar la situación real de la lista vinculada.

 Devuelve  si hay un ciclo en la lista vinculadatrue  . En caso contrario, regresa  false .

Ejemplo:

Entrada: cabeza = [3,2,0,-4], pos = 1

Salida: verdadero (el nodo tiene un ciclo)

(2) Solución

Análisis de ideas: podemos utilizar punteros rápidos y lentos para analizar este problema.

1. Primero define rápido y lento para apuntar a la cabeza.

2. El puntero rápido toma dos pasos a la vez y el puntero lento toma un paso a la vez.

3. Si no hay un bucle en la lista vinculada, rápido eventualmente apuntará a nulo. Si hay un bucle en la lista vinculada, después de que tanto rápido como lento ingresen al bucle, ya que rápido toma un paso más que lento cada vez, rápido eventualmente se pondrá al día con la lentitud.

Código:

public class Solution {
    public boolean hasCycle(ListNode head) {
        //先判断特殊情况
        //若链表中为空或链表中只有一个元素,
        // 则链表中无环
        // 直接返回false
        if (head == null || head.next == null) {
            return false;
        }
        //定义快指针和慢指针
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;//fast一次走两步
            slow = slow.next;//slow一次走一步
            if (fast == slow) {//fast与slow相遇,表明链表带环
                return true;
            }
        }
        //fast指向空或指向尾节点,表明链表不带环
        return false;
    }
}

pensar:

1. En la condición while, ¿se puede modificar a while(fast.next!= null && fast!= null)?

No, el operador && (lógico AND) se juzga de izquierda a derecha . Cuando la expresión de la izquierda es falsa, la expresión de la derecha no se evaluará y se devolverá falso directamente. Solo cuando la expresión de la izquierda es verdadera, el Se evaluará la expresión de la derecha. Fórmula, si la expresión de la derecha es verdadera, devuelve verdadero.

En la condición while, primero debe determinar si el punto señalado por fast es nulo. Si el punto señalado por fast es nulo, no se puede acceder al siguiente nodo a través de fast.next; de lo contrario , se generará una NullPointerException (excepción de puntero nulo) . Por lo tanto, mientras (fast! = null && fast.next! = null), cuando fast apunta a nulo, devuelve falso directamente y ya no ejecuta fast.next.

2. Cuando lo rápido toma dos pasos y lo lento toma un paso, ¿puede lo rápido alcanzar a lo lento? Cuando lo rápido toma tres o cuatro pasos... ¿puede alcanzar a lo lento?

Cuando la lista vinculada es acíclica, fast puede juzgar que la lista vinculada es acíclica tomando dos o tres pasos...

Cuando hay un anillo en la lista vinculada, asumimos que rápido toma x ( x >= 2 ) pasos cada vez, hay nodos desde la cabeza hasta el nodo de entrada del anillo (se necesita un paso para ir desde la cabeza hasta la entrada del anillo nodo), y hay C nodos en total en el anillo, cuando slow llega al punto de entrada del bucle, fast y slow están separados por N ( 0 <= N < C ) nodos .

Cuando N = 0, rápido y lento se encuentran directamente en el punto de entrada del bucle, por lo que consideramos el caso de N > 0 y N < C

Cuando la lentitud llega al punto de entrada, es lo que a menudo llamamos el problema de recuperación . La fórmula para el problema de recuperación es:

Tiempo de recuperación (número de veces) = diferencia de distancia/diferencia de velocidad

Suponemos que el número de recuperaciones (es decir, cuántas veces lo rápido ha alcanzado a lo lento) es m, la diferencia de distancia entre rápido y lento es N y la diferencia de velocidad es x-1, entonces m = N / x-1 ,

Cuando x = 2, m = N, es decir, lo rápido puede alcanzar lo lento caminando N veces.

Cuando x = 3, m = N / 2. Cuando N es un número par, lo rápido puede alcanzar a lo lento después de caminar N/2 veces ; cuando N es un número impar, lo rápido puede alcanzar a lo lento después de caminar (N-1 )/2 veces. La distancia entre rápido y lento es 1. Dado que el rápido toma tres pasos y el lento toma un paso , el rápido superará al lento en 1 paso.

En este momento, la distancia N entre rápido y lento se convierte en C-1 , y lo rápido comienza a alcanzar a lo lento nuevamente. Si C-1 es un número par, después de (C-1)/2 veces, lo rápido alcanza a lo lento. ; si C-1 es un número impar, cuando la distancia entre rápido y lento es 1, el rápido vuelve a adelantar al lento , la distancia entre rápido y lento vuelve a ser C-1, el rápido alcanza al lento otra vez... En este ciclo, Lo rápido nunca puede alcanzar a lo lento.

 Por lo tanto, podemos ver que cuando lo rápido toma tres pasos a la vez y lo lento toma un paso a la vez, es posible que lo rápido no necesariamente pueda alcanzar a lo lento.

Cuando lo rápido toma cuatro pasos a la vez y lo lento toma un paso a la vez, la idea de análisis es consistente con lo rápido que toma tres pasos a la vez. Lo rápido puede superar a lo lento en uno o dos pasos. En este momento, la distancia entre lo rápido y lo lento es uno o dos pasos. lento se convierte en C-1 o C-2, y luego continúa analizando situación por situación

3. ¿Qué sucede cuando das tres pasos rápido y dos pasos lento, o cuatro pasos rápido y tres pasos lento?

A partir del tiempo de recuperación (número de veces) = diferencia de distancia/diferencia de velocidad, se puede obtener

Cuando lo rápido toma tres pasos y lo lento toma dos, o lo rápido toma cuatro pasos a la vez y lo lento toma tres pasos a la vez, la diferencia de velocidad entre rápido y lento es 1. Lo rápido definitivamente puede alcanzar a lo lento , pero

Cuando el rápido da dos pasos a la vez y el lento da un paso a la vez, la distancia total recorrida por el lento es N, es decir, el rápido puede alcanzar al lento dentro de un círculo.

Cuando el rápido toma tres pasos a la vez y el lento toma dos pasos a la vez, la distancia total recorrida por el lento es 2*N (2*N puede ser mayor que C), por lo que el rápido no necesariamente alcanza al lento dentro de un círculo . .

Esta pregunta proviene de:

141. Lista enlazada de anillos - LeetCode

2. El punto de entrada de la lista circular enlazada.

(1) Descripción del título

Dado el nodo principal de una lista vinculada   head , devuelve el primer nodo donde la lista vinculada comienza a entrar en el bucle. Si la lista vinculada no tiene bucles, devuélvala  null.

Si hay un nodo en la lista vinculada al que se puede  next acceder nuevamente siguiendo continuamente el puntero, entonces hay un ciclo en la lista vinculada. Para representar un anillo en una lista vinculada determinada, el sistema de evaluación utiliza internamente un número entero  pos para representar la posición en la lista vinculada donde está conectada la cola de la lista vinculada ( índice que comienza desde 0 ). Si  pos es así  -1, entonces no hay ningún ciclo en la lista vinculada. Nota: pos  no se pasa como parámetro , solo se usa para identificar la situación real de la lista vinculada. No se permite la modificación de la lista enlazada.

Ejemplo

Entrada: cabeza = [1,2], pos = 0

Salida: Devuelve el nodo de la lista vinculada con índice 0

(2) Solución

Análisis de ideas: para determinar si hay un bucle en la lista vinculada, utilizamos el método de punteros rápidos y lentos para determinar si hay un bucle en la lista vinculada. Lo rápido toma dos pasos a la vez y lo lento toma un paso a la vez. Si hay un bucle en la lista vinculada, lo rápido definitivamente puede alcanzar a lo lento en el bucle. Con base en esta conclusión, podemos continuar deduciendo otra conclusión: el puntero que comienza en el nodo principal se encontrará y entrará en el punto de bucle con el puntero que comienza en los nodos de encuentro rápido y lento (prueba al final)

Por lo tanto, encuentre el punto de entrada:

1. Determine si hay un bucle en la lista vinculada. Si no hay bucle, devuelva nulo. Si hay un bucle, busque el nodo donde se encuentran lo rápido y lo lento.

2. Deje que los dos punteros comiencen desde el nodo principal y el punto de encuentro respectivamente. Cuando los dos punteros se encuentran, es el punto de entrada de la lista vinculada.

Código:

public class Solution {
    public ListNode detectCycle(ListNode head) {
        //链表为空,直接返回null
        if(head == null){
            return null;
        }
        ListNode fast = head;
        ListNode slow = head;
        while(fast.next != null && fast.next.next != null){
            fast = fast.next.next;
            slow = slow.next;
            //找到fast与slow的相遇点
            if(fast == slow){
                //让fast从头节点出发,
                //slow从相遇点出发,
                fast = head;
                while(fast != slow){
                    fast = fast.next;
                    slow = slow.next;
                }//相遇节点即为入环节点
                     return slow;
            }
        }
        //链表中无环,返回null
        return null;
    }
}

¿Por qué el puntero que comienza en el nodo principal siempre se fusiona con el puntero que comienza en los nodos de encuentro rápido y lento y ingresa al punto de bucle?

De manera similar, cuando hay un anillo en la lista vinculada, asumimos que hay nodos desde la cabeza hasta el nodo de entrada del anillo (se requiere un paso desde la cabeza hasta el nodo de entrada del anillo) y que hay nodos C en el anillo. rápido y lento se encuentran, habrá un nodo con el nodo de entrada del anillo. La distancia es d

Cuando lo rápido y lo lento se encuentran,

La distancia recorrida por rápido es a + n*C + d

¿Por qué la distancia recorrida por el rápido a + n*C + d ?

a es la distancia desde el nodo principal hasta el nodo de entrada al anillo. Cuando a > C, es posible que lento aún no haya entrado al anillo, pero rápido ya ha recorrido muchos círculos en el anillo . Suponemos que cuando rápido y lento se encuentran, rápido es caminando en el ring n veces, por lo que la distancia recorrida rápidamente es a + n*C + d

La distancia recorrida por lento es a + d

Dado que rápido es dos veces más rápido que lento, por lo tanto

2*(a + d)= a + n*C + d,即 a = n*C - d

Convierta n*C a C*(n-1) + C, y la fórmula se puede convertir en a =C* (n-1) + Cd ,

a es la distancia desde el nodo principal hasta el punto de entrada del bucle, C - d es la distancia desde el nodo de encuentro hasta el punto de entrada del bucle,

a =C* (n-1) + nd muestra que el puntero que parte del nodo principal da un paso igual al puntero que parte del nodo de encuentro. Después de caminar (n-1) círculos, regresa al punto de encuentro y luego camina una distancia de n - d., es decir, el puntero que comienza desde el nodo principal y el puntero que comienza desde los nodos de encuentro rápido y lento al mismo tiempo eventualmente se encontrarán en el punto de entrada del bucle.

La pregunta viene de:

142. Lista circular enlazada II - LeetCode

Supongo que te gusta

Origin blog.csdn.net/2301_76161469/article/details/133188387
Recomendado
Clasificación