一 判断是否有环的思路--快慢指针
首先创建两个指针1和2(在java里就是两个对象引用),同时指向这个链表的头节点。然后开始一个大循环,在循环体中,让指针1每次向下移动一个节点,让指针2每次向下移动两个节点,然后比较两个指针指向的节点是否相同。如果相同,则判断出链表有环,如果不同,则继续下一次循环。
例如链表A->B->C->D->B->C->D,两个指针最初都指向节点A,进入第一轮循环,指针1移动到了节点B,指针2移动到了C。第二轮循环,指针1移动到了节点C,指针2移动到了节点B。第三轮循环,指针1移动到了节点D,指针2移动到了节点D,此时两指针指向同一节点,判断出链表有环。
此方法也可以用一个更生动的例子来形容:在一个环形跑道上,两个运动员在同一地点起跑,一个运动员速度快,一个运动员速度慢。当两人跑了一段时间,速度快的运动员必然会从速度慢的运动员身后再次追上并超过,原因很简单,因为跑道是环形的。
二 找出环的入口节点(环的连接点)的思路
这里先证明一个定理:碰撞点到连接点的距离=头指针到连接点的距离
假设单链表的总长度为L,头结点到环入口的距离为a,环入口到快慢指针相遇的结点距离为x,环的长度为r,慢指针总共走了s步,则快指针走了2s步。另外,快指针要追上慢指针的话快指针至少要在环里面转了一圈多(假设转了n圈加x的距离),得到以下关系:
s = a + x
2s = a + nr + x
=>a + x = nr
=>a = nr - x
由上式可知:若在头结点和相遇结点分别设一指针,同步(单步)前进,则最后一定相遇在环入口结点。
代码如下:
public ListNode EntryNodeOfLoop(ListNode pHead)
{
ListNode slow = pHead;
ListNode fast = pHead;
boolean isLoop = false; //判断是否有环
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
isLoop = true;
break;
}
}
if (isLoop) {
slow = pHead;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
return null;
}
public ListNode EntryNodeOfLoop(ListNode pHead)
{
if (pHead==null||pHead.next==null) {
return null;
}
ListNode slow = pHead;
ListNode fast = pHead;
while (fast!=null&&fast.next!=null) {
slow = slow.next;
fast = fast.next.next;
if (slow==fast) {
slow = pHead;
while (slow!=fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
return null;
}
package 秋招刷题.剑指offer_链表;
/*https://www.nowcoder.com/discuss/198840?type=0&order=0&pos=65&page=2
使用双指针,一个指针 fast 每次移动两个节点,一个指针 slow 每次移动一个节点。
因为存在环,所以两个指针必定相遇在环中的某个节点上。假设相遇点在下图的 z1 位置,
此时 fast 移动的节点数为 x+2y+z,slow 为 x+y,由于 fast 速度比 slow 快一倍,
因此 x+2y+z=2(x+y),得到 x=z。
在相遇点,slow 要到环的入口点还需要移动 z 个节点,
如果让 fast 重新从头开始移动,并且速度变为每次移动一个节点
,那么它到环入口点还需要移动 x 个节点。在上面已经推导出 x=z,
因此 fast 和 slow 将在环入口点相遇。
*/
public class 链表中环的入口节点 {
public static void main(String[] args) {
}
public static ListNode EntryNodeOfLoop(ListNode pHead)
{ if (pHead == null || pHead.next == null)
return null;
ListNode fast = pHead, slow = pHead;
do {
fast = fast.next.next;
slow = slow.next;
}while (slow != fast); //找到相遇点
fast = pHead;
while (fast != slow) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
}