LeetCode 142. 环形链表
快慢指针,能相遇则说明有环
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                // 说明存在环
                slow = head;
                break;
            }
        }
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                // 第二次相遇
                return slow;
            }
        }
        return slow;
    }
}
给定一个链表的头节点 head,返回链表开始入环的第一个节点。如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
思路分析
- 判断链表是否为空
 判断链表是否有环
- 有环,当第一次相遇时,将其中一个指针指向头节点,随后相同步伐后移,再次相遇时就是环的第一个节点
 
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                // 说明存在环
                break;
            }
        }
        if (fast==null || fast.next == null) return null;
        slow = head;
        while (fast != slow) {
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }
}
错误代码解析
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        if (fast==null || fast.next == null) return null;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                // 说明存在环
                break;
            }
        }
        slow = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next;
            if (slow == fast) {
                // 第二次相遇
                break;
            }
        }
        return slow;
    }
}
第二次的 while (fast != null && fast.next != null) 循环存在逻辑上的错误
正确的 Floyd 判圈算法流程
检测环是否存在:
- 使用两个指针,
fast和slow,fast每次移动两步,slow每次移动一步。如果存在环,fast和slow会在环内相遇;如果没有环,fast会先到达链表的末尾,说明链表无环。 
- 使用两个指针,
 找到环的起点:
- 当 
fast和slow第一次在环内相遇时,slow被重置回链表的头节点,而fast保持在相遇点。接着,slow和fast每次都只移动一步,直到它们再次相遇。第二次相遇的节点就是环的起点。 
- 当 
 
第二次的 while 循环
while (fast != null && fast.next != null) {
    slow = slow.next;
    fast = fast.next;
    if (slow == fast) {
        // 第二次相遇
        break;
    }
}
1. 误用的循环条件
 while (fast != null && fast.next != null)这个检查在第一次循环结束后已经不再需要了。实际上,第一次循环已经确定了链表是否有环,并且如果存在环,fast 和 slow 已经在环内相遇。
问题是,这段代码重新使用相同的条件来检查 fast 和 slow,试图寻找第二次相遇,这不符合 Floyd 算法的设计。在寻找环的起点时,不需要再检查 fast != null && fast.next != null,因为在有环的情况下,fast 永远不会变为 null。
2. 不正确的步进方式
在寻找环的起点时,您让 slow 和 fast 以不同的速度移动:
slow = slow.next;
fast = fast.next;
- 问题:在 Floyd 算法中,当找到相遇点后,
slow和fast必须以相同的步长(即都每次移动一步)来确保它们在环的起点相遇。 - 第二次相遇时,它们应该同时从链表头和相遇点出发,一步步移动。而这段代码让 
fast和slow分别按照两种速度移动,破坏了同步相遇的原则。 
因此,在此代码中,即使它们第一次相遇了,第二次的步进方式也无法正确找到环的起点,最终无法保证程序的正确性。
3. 缺少返回值逻辑
此外,您的代码在第二次 while 循环中的 if (slow == fast) 判断后并没有返回任何值,而是仅仅用 break 退出循环,这样会导致后续的 return slow; 可能返回了一个错误的值。
正确的第二次相遇逻辑
在正确的 Floyd 判圈算法中,第二次的 while 循环应该这样写:
slow = head; // 将 slow 重置为链表头
while (slow != fast) { // 快慢指针同步移动
    slow = slow.next;
    fast = fast.next;
}
return slow; // 返回环的起点
slow从链表的头节点开始,fast从相遇点开始,它们每次都只移动一步,直到它们再次相遇时,相遇点就是环的起点。- 在这个过程里,我们不需要再检查 
fast != null,因为在有环的情况下,fast不会变成null。