Detección de anillos en listas enlazadas

Detección de anillos en listas enlazadas

Defina dos punteros rápido y lento pFast y pSlow. PFast da dos pasos a la vez y pSlow da un paso a la vez. Si pFast es nulo después del recorrido del bucle, no hay ningún anillo en la lista vinculada. Si pFast y pSlow se encuentran, hay un anillo en la lista enlazada. No digas tonterías, solo mira el código:

public class DetectLoop {
    
    
    public static void main(String[] args) {
    
    
        int[] arr = {
    
    12, 13, 14};
        int[] loopArr = {
    
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
        DetectLoop linkedListDemo = new DetectLoop();
        Node head = linkedListDemo.buildLoopLinkedList(arr, loopArr);

        boolean detectFalg = linkedListDemo.detectLoop(head);
        System.out.println("detect loop: " + detectFalg);
    }

    private static class Node {
    
    
        final Integer item;
        Node next;

        Node(Integer item, Node next) {
    
    
            this.item = item;
            this.next = next;
        }
    }

    /**
     * 通过数组构造一个带有环的链表
     *
     * @param arr
     * @return
     */
    public Node buildLoopLinkedList(int[] arr, int[] loopArr) {
    
    
        Node head = new Node(arr[0], null);
        Node p = head;
        if (arr.length >= 2) {
    
    
            for (int i = 1; i < arr.length; i++) {
    
    
                Node temp = new Node(arr[i], null);
                p.next = temp;
                p = temp;
            }
        }

        // 构造一个环形链表
        Node loopHead = new Node(loopArr[0], null);
        if (loopArr.length >= 2) {
    
    
            Node q = loopHead;
            for (int i = 1; i < loopArr.length; i++) {
    
    
                Node temp = new Node(loopArr[i], null);
                q.next = temp;
                q = temp;
            }
            q.next = loopHead;
        }
        p.next = loopHead;
        return head;
    }

    /**
     * 检查是否存在环形链表
     *
     * @param head
     * @return
     */
    public boolean detectLoop(Node head) {
    
    
        Node pSlow = head, pFast = head;
        boolean detectFlag = false;
        // 只包含一个结点
        if (head.next == null) {
    
    
            return detectFlag;
        }

        List<Integer> slowPassNodes = new ArrayList<>();
        List<Integer> fastPassNodes = new ArrayList<>();

        while (true) {
    
    
            pSlow = pSlow.next;
            pFast = pFast.next.next;
            slowPassNodes.add(pSlow.item);
            if (pFast != null) {
    
    
                fastPassNodes.add(pFast.item);
            }
            if (pFast == null) {
    
    
                break;
            }
            if (pSlow == pFast) {
    
    
                detectFlag = true;
                break;
            }
        }
        System.out.println("slow pointer traverse node list: " + slowPassNodes);
        System.out.println("fast pointer traverse node list: " + fastPassNodes);
        return detectFlag;
    }
}

Se construye una lista enlazada circular en el código:
Inserte la descripción de la imagen aquí

¿Por qué pFast y pSlow se encuentran?

  1. Hay una diferencia de un paso entre el puntero rápido y el puntero lento. En este momento, continúe caminando hacia atrás, el puntero lento avanza un paso y el puntero rápido avanza dos pasos, y los dos se encuentran;
  2. Hay una diferencia de dos pasos entre el puntero rápido y el puntero lento. En este momento, continúe caminando hacia atrás, el puntero lento avanza un paso y el puntero rápido avanza dos pasos. Hay una diferencia de un paso entre los dos, que se transforma en la primera situación;
  3. Hay una diferencia de N pasos entre el puntero rápido y el puntero lento. En este momento, continúe retrocediendo más, a baja velocidad antes de que el puntero avance dos pasos, una diferencia de fase entre los dos (N + 1 - 2) -> N - 1pasos, eventualmente se convertirá en una situación. Así que los indicadores rápidos deben cumplir con los indicadores lentos. Y debido a que la velocidad del puntero rápido es el doble que la del puntero lento, solo debe hacer un círculo cuando se encuentre.

Referencia:
¿Por qué utilizar los punteros rápido y lento para encontrar el anillo de la lista enlazada, el puntero rápido y el puntero lento deben coincidir?
Detección de ciclo en lista enlazada con el enfoque de liebre y tortuga

Encuentra el nodo inicial en la lista vinculada

Citando una imagen en Explique cómo funciona la búsqueda del nodo de inicio del ciclo en la lista vinculada al ciclo:

Inserte la descripción de la imagen aquí
La longitud de pSlow es x + y,
la longitud de pFast es (x + y + z) + y = x + 2y + z,
La velocidad de pFast es el doble que la de pSlow, por lo que hay: 2 (x + y) = x + 2y + z => x + 2y + z = 2x + 2y => x = z, x representa la parte no cíclica de la lista vinculada Longitud, apunte el puntero pSlow al nodo principal de la lista enlazada y mueva pSlow y pFast al mismo tiempo hasta que se encuentren, que es el nodo inicial del anillo.

En el punto de encuentro, puede arreglar pFast y dejar que pSlow continúe moviéndose hasta que pSlow y pFast se encuentren El número de pasos que da pSlow es la longitud del bucle.

Mira el código:

 public Node findCycleHead(Node head) {
    
    
        Node pSlow = head, pFast = head;
        // 只包含一个结点
        if (head.next == null) {
    
    
            return null;
        }

        while (true) {
    
    
            pSlow = pSlow.next;
            pFast = pFast.next.next;
            if (pFast == null) {
    
    
                return null;
            }
            if (pSlow == pFast) {
    
    
                break;
            }
        }

        // pFast 指向头结点
        pFast = head;
        int i = 0;
        while (pFast != pSlow) {
    
    
            pFast = pFast.next;
            pSlow = pSlow.next;
            i++;
        }
        System.out.println("非环部分长度:" + i);
        return pFast;
    }

Supongo que te gusta

Origin blog.csdn.net/jiaobuchong/article/details/84727007
Recomendado
Clasificación