题目
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
分析
快慢指针,快指针一次两步慢指针一次一步。当快指针走到空时证明无环。
如果有环,快慢指针必相遇。
相遇后快指针回到起点,和慢指针一起走,再次相遇时为入口。
环外链表的长度 = 第一次相遇点 + n-1次环的长度
(公式推导)
代码
public class ListNode{
int val;
ListNode next;
public ListNode(int data) {
this.val = data;
next=null;
}
}
public ListNode detectCycle(ListNode head) {
ListNode p1 = head;
ListNode p2 = head;
/*p1和p2定义的节点相等所以不能用这个判断。
用第一个while循环得到的相遇点会导致寻找入环点增加循环次数。
* */
/*while(p1!=p2){
if(p1==null||p1.next==null){
return null;
}
p1 = p1.next.next;
p2 = p2.next;
}*/
while (true) {
if(p1.next==null || p1.next.next==null) return null;
p1 = p1.next.next;
p2 = p2.next;
if (p1 == p2) break;
}
p1=head;
while(p1!=p2){
p1 = p1.next;
p2 = p2.next;
}
return p1;
/*快慢指针有两种用法:第一是让快指针走到结尾确定慢指针的位置。代码:
ListNode p1 = head;
ListNode p2 = head;
while(p2.next!=null&&p2.next.next!=null){
p1 = p1.next;
p2 = p2.next.next;
}
有时快慢指针最终的位置是中间位置加1或者减1,那么在定义的时候就更改。
第二是找到快慢指针相遇的位置。代码:
while (true) {
if(p1.next==null || p1.next.next==null) return null;
p1 = p1.next.next;
p2 = p2.next;
if (p1 == p2) break;
}
因为习惯定义两个节点都是head,所以采用true的判定方法
* */
}