Given a linked list, return the node where the cycle begins. If there is no cycle, return null
.
Note: Do not modify the linked list.
Follow up:
Can you solve it without using extra space?
分析:题目要求判断返回一个链表中环的入口节点,没有入口节点话就返回null。
思路:Step1:首先根据Linked List Cycle判断是否有环,没有环的话就直接返回null;否则设快慢指针碰撞的节点为p,转Step2;
Step2:如果有环,则假设慢指针走过的节点个数是s,则快指针走过的节点个数是2s;再假设环上的节点个数是c,则2s=s+nc(从这个地方也可以得到s = nc);再假设头结点到环的入口节点经过的节点个数是a(包括头结点和入口节点),假设入口节点.next到快慢指针碰撞点p上经过的节点个数是x,则a+x=s,进一步推导得a+x=nc , a=(n-1)c+c-x ,其中(c-x)是p.next向后走到入口节点经过的节点个数,n表示大于1的整数。则头结点和p.next同时向后走,两个指针相遇的地方就是环的入口节点。
/*
*判断一个链表是否有环,有环的话输出入口节点
*/
public ListNode detectCycle(ListNode head) {
/*Step1:先找到链表中是否有环,有环的话通过快指针和慢指针找到其碰撞的节点*/
if(head == null || head.next == null){
return null;
}
ListNode slow = head;
ListNode fast = head.next;
while(fast!=null && fast.next!=null){
if(fast == slow){
break;
}
fast = fast.next.next;//每次fast走两步,slow走一步,如果有环,则fast和slow一定相遇
slow = slow.next;
}
if(fast != slow){//fast不等于slow,说明没有环
return null;
}else{
/*Step2:找入口节点:头结点到入口节点的距离等于碰撞点的下一个节点p到入口点的距离+(n-1)c,其中c表中环的长度*/
ListNode p = fast.next;//设置p为碰撞点的下一个节点
ListNode h = head;
while(h!=null && p!=null){
if(p == h){
break;
}
h=h.next;
p = p.next;
}
return h;
}
}
规整代码之后再次提交。
public ListNode detectCycle(ListNode pHead) {
/*Step1:判断链表是否有环,有环的话则返回快慢指针的碰撞点,没有环的话则返回null*/
ListNode meetNode = hasCycle(pHead);
if(meetNode == null){
return null;
}
/*Step2:找入口节点:头结点到入口节点的距离等于碰撞点的下一个节点p到入口点的距离+(n-1)c,其中c表中环的长度*/
ListNode p = meetNode.next;//设置p为碰撞点的下一个节点
ListNode h = pHead;
while(p != h){
h=h.next;
p = p.next;
}
return h;
}
/**
* 判断链表中是否有环。
* 有环的话返回快慢指针的碰撞点,没有环的话则返回null.
*/
public ListNode hasCycle(ListNode head) {
if(head == null || head.next == null){
return null;
}
ListNode slow = head;
ListNode fast = head.next;
while(fast!=null && fast.next!=null){
if(fast == slow){
break;
}
fast = fast.next.next;//每次fast走两步,slow走一步,如果有环,则fast和slow一定相遇
slow = slow.next;
}
return fast==slow?fast:null;
}
网上有一篇不错的关于介绍链表中环相关问题的文章。http://blog.sina.com.cn/s/blog_725dd1010100tqwp.html
从另外一个角度,使用两个指针和环的节点个数来解决。
/**
* 从另外一个角度考虑,继续使用两个指针的思想。
* 在找到碰撞点之后,可以计算得到环中结点的个数c,然后让p从头指针开始先走c步,然后q从头指针再与p一起向后走,
* p和q相遇的地方即为链表中环的入口点。
*/
public ListNode detectCycle(ListNode pHead) {
/*Step1:判断链表是否有环,有环的话则返回快慢指针的碰撞点,没有环的话则返回null*/
ListNode meetNode = hasCycle(pHead);
if(meetNode == null){
return null;
}
/*Step2:计算环中结点个数*/
ListNode p = meetNode.next;//设置p为碰撞点的下一个节点
int c = 1;
while(p != meetNode){
c++;
p = p.next;
}
/*Step3:p从头指针先走c步,然后q从头指针开始和p一直走,相遇点即为环的入口点*/
p = pHead;
for(int i=0;i<c;i++){
p = p.next;
}
ListNode q = pHead;
while(p != q){
q=q.next;
p = p.next;
}
return q;
}
/**
* 判断链表中是否有环。
* 有环的话返回快慢指针的碰撞点,没有环的话则返回null.
*/
public ListNode hasCycle(ListNode head) {
if(head == null || head.next == null){
return null;
}
ListNode slow = head;
ListNode fast = head.next;
while(fast!=null && fast.next!=null){
if(fast == slow){
break;
}
fast = fast.next.next;//每次fast走两步,slow走一步,如果有环,则fast和slow一定相遇
slow = slow.next;
}
return fast==slow?fast:null;
}