解法1:边遍历边删除(边标记)


public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead) {
        if (pHead == null || pHead.next == null) {
            return null;
        }
        if (pHead.next == pHead) {
            return pHead;
        }
        ListNode next = pHead.next;
        pHead.next = pHead;
        return EntryNodeOfLoop(next);
    }
}

删除就是说让这个节点的next指向这个节点本身,当然也可以用其他的方法来做标记

如果遍历到某个节点,发现它已经被删除了,那就是环的第一个节点

时间复杂度O(n)

空间复杂度O(1)

解法2:哈希表

不考虑空间复杂度的情况下可以用这个

import java.util.*;
public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead) {
        if (pHead == null || pHead.next == null) {
            return null;
        }
        Set<ListNode> visited = new HashSet<ListNode>();
        while (pHead != null) {
            if (visited.contains(pHead)) {
                return pHead;
            }
            visited.add(pHead);
            pHead = pHead.next;
        }
        return null;
    }
}

时间复杂度O(n)

空间复杂度O(n)

解法3:快慢指针

思路:

快指针fast一次走2步,慢指针slow一次走一步,快慢指针相遇时说明有环的存在。

假设在环外有a个节点,在环内走了b个节点, 慢指针还有c个节点没有走

那么相遇的时候,快指针走了a+b+c+b, 慢走了a+b

因为快指针走的是慢指针的两倍,所以a + b + c + b =2 (a + b) = a + b + a + b,也就是c = a;

慢指针再走c步就能到环的入口,也就是再走a步即可;

让快指向头节点,再重新一步一步走,当快指针和慢指针相遇的时候,慢指针走了a步,也正好到入口节点

public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead) {
        if (pHead == null || pHead.next == null) {
            return null;
        }
        ListNode slow = pHead.next;
        ListNode fast = pHead.next.next;
        while (fast != null && slow != fast) {
            slow = slow.next;
            if (fast.next != null) {
                fast = fast.next.next;
            } else {
                return null;
            }
        }
        if (fast == slow ) {
            fast = pHead;
            while (fast != slow) {
                fast = fast.next;
                slow = slow.next;
            }
        }

        return fast;

    }
}