Given a linked list, return the node where the cycle begins. If there is no cycle, return null
.
Follow up:
Can you solve it without using extra space?
分析:
题目中要求求出一个循环链表的第一个循环结点,这个题目还要求不能外加空间。
其实这个题可以把他分成两步来做,一个是判断一个链表是不是有环,另一个是求两个链表的第一个交点
判断两个链表是不是有环的存在,就是两个指针,一个快,一个慢,如果两个指针能相遇,那就说明这个链表有环,否则没有环
求两个相交链表的第一个交点,可以分别遍历两个链表,求出其长度,然后让长链表先走对应的两个链表长度的差值步,然后两个链表同时走,
并判断俩结点是否相同,如果相同就返回这个结点
下面是上述思路的代码:
public class LinkedListCycleII {
public ListNode detectCycle(ListNode head) {
if(head == null)
return null;
//判断是否存在环
ListNode node = head;
ListNode fast = node,slow = node;
while(fast.next != null && fast.next.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow)
break;
}
//无环
if(fast.next == null || fast.next.next == null)
return null;
//有环,需要断开环
fast = fast.next;
slow.next = null;
//现在有了两个链表,一个是head开头,一个是fast开头
//找到两个链表的第一相交的结点
//首先依次遍历两个链表,确定两个链表的长度,得到count = l1 - l2 的值,然后长链表先走
//count步,之后两个同时走,并判断两个结点是否相同,如果相同就是第一个相交的结点
return FindFirstNode(head,fast);
}
public ListNode FindFirstNode(ListNode l1,ListNode l2){
//遍历两个链表,分别求长度
int len1 = GetListLength(l1);
int len2 = GetListLength(l2);
int count = len1 - len2;
if(count < 0){
count = - count ;
//令长链表先走,第一个变量放置长链表
return FindNode(l2,l1,count);
}
else
return FindNode(l1,l2,count);
}
public int GetListLength(ListNode head){
int len = 0;
ListNode node = head;
while(node != null){
len ++;
node = node.next;
}
return len;
}
public ListNode FindNode(ListNode h1,ListNode h2,int count){
ListNode l1 = h1,l2 = h2;
while(count-- != 0){
l1 = l1.next;
}
while(l1 != null && l2 != null && l1 != l2){
l1 = l1.next;
l2 = l2.next;
}
ListNode result = l1;
return result;
}
}
思路2:
上述思路简单明了,就是实现起来代码比较繁琐,当时我在A这个题的过程中,会遇到很多意想不到的错误,
下面就给出网上的另一种思路,真的是分析透彻了之后再代码实现会让代码更美
思路是这样的
首先当一个快指针和一个满指针在环内循环的时候,在他俩相遇之前,慢指针是不会循环一圈的,假设slow指针走了s步,那么fast指针就走了2s步,当然这其中fast指针走了n圈,假设一圈有r步,外加S步,假设这个链表的圈内共有r步,那么2s = s + nr
可以得出s = nr,由此可以看出当这个链表就只是一个环的时候,快慢指针会在head出相遇,fast指针转两圈,slow指针刚好转一圈
同时,我们假设从环的入口点到相遇点共有a步,从起点到环的入口点为x,整个链表的长度为L,则慢指针走的s步就为x+a, 那么:x+a = nr
= (n-1)r+r = (n-1)r + (L-x)
所以x = (n-1)r + (L-x-a)
L-x-a正好是从相遇点到环的入口点的距离, 那么从链表的头结点到环的入口结点的距离就等于(n-1)圈环内距离加上从相遇点到环的入口距离,
那么可以再设置一个指针slow2从链表的头结点开始,slow和slow2指针同时走一步,知道两个指针相同,就是环的入口地址
下面是代码:
public ListNode detectCycle2(ListNode head){
ListNode fast = head,slow = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if(fast == slow){
ListNode slow2 = head;
while(slow != slow2){
slow = slow.next;
slow2 = slow2.next;
}
return slow;
}
}
return null;
}