Detection of rings in linked lists

Detection of rings in linked lists

Define two fast and slow pointers pFast and pSlow. pFast takes two steps at a time and pSlow takes one step at a time. If pFast is null after the loop traversal, there is no ring in the linked list. If pFast and pSlow meet, there is a ring in the linked list. Don't talk nonsense, just look at the code:

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;
    }
}

A circular linked list is constructed in the code:
Insert picture description here

Why do pFast and pSlow meet?

  1. There is one step difference between the fast pointer and the slow pointer. At this time, continue walking backward, the slow pointer moves forward one step, and the fast pointer moves forward two steps, and the two meet;
  2. There is a difference of two steps between the fast pointer and the slow pointer. At this time, continue to walk backward, the slow pointer forwards one step, and the fast pointer advances two steps. There is a difference of one step between the two, which is transformed into the first situation;
  3. There is a difference of N steps between the fast pointer and the slow pointer. At this time, continue to go back further, slow speed before the pointer is advanced two steps pointer, a phase difference between the two (N + 1 - 2) -> N - 1steps, it will eventually be converted to a situation. So fast pointers must meet slow pointers. And because the speed of the fast pointer is twice that of the slow pointer, it must only make a circle when meeting.

Reference:
Why use the fast and slow pointers to find the ring of the linked list, the fast pointer and the slow pointer must meet?
Cycle detection in linked list with the Hare and Tortoise approach

Find the starting node in the linked list

Quoting a picture in Explain how finding cycle start node in cycle linked list work?:

Insert picture description here
The length of pSlow is x + y,
the length of pFast is (x + y + z) + y = x + 2y + z,
The speed of pFast is twice that of pSlow, so there is: 2(x+y) = x+2y+z => x+2y+z = 2x+2y => x=z, x represents the non-cyclic part of the linked list Length, point the pSlow pointer to the head node of the linked list, and move pSlow and pFast at the same time until they meet, which is the starting node of the ring.

At the meeting-point, you can fix pFast and let pSlow continue to move until pSlow and pFast meet. The number of steps taken by pSlow is the length of the loop.

Look at the code:

 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;
    }

Guess you like

Origin blog.csdn.net/jiaobuchong/article/details/84727007