对于一个给定的链表,返回环的入口节点,如果没有环,返回null
拓展:
你能给出不利用额外空间的解法么?
package linkedlist; /** * 题目描述: 链表的入环节点,如果无环,返回null * Given a linked list, return the node where the cycle begins. If there is no cycle, returnnull. * Follow up: Can you solve it without using extra space? * 思路: * 1)首先判断是否有环,有环时,返回相遇的节点,无环,返回null * 2)有环的情况下, 求链表的入环节点 * fast再次从头出发,每次走一步, * slow从相遇点出发,每次走一步, * 再次相遇即为环入口点。 * 注:此方法在牛客BAT算法课链表的部分有讲解。 */ //nowcoder pass public class Solution { public ListNode detectCycle(ListNode head) { if (head == null) { return null; } ListNode meetNode = meetingNode(head); if (meetNode == null) {//说明无环 return null; } ListNode fast = head; ListNode slow = meetNode; while (slow != fast) { slow = slow.next; fast = fast.next; } return slow; } //寻找相遇节点,如果无环,返回null public ListNode meetingNode(ListNode head) { ListNode slow = head; ListNode fast = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; if (slow == fast) { return slow; } } return null; } }
来个不一样的思路:
1.首先判断是否存在环
2.若存在环,则从起点开始,每走一步就删除上一个节点的next指针,最后一个节点就是环的起点。因为环的起点会存在两个next指向它。
ListNode *detectCycle(ListNode *head) {//每次前进的时候删除上一个指针
if(head==NULL||head->next==NULL)
return NULL;
ListNode*fast=head;
ListNode*slow=head;
while(fast!=NULL&&fast->next!=NULL){
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
break;
}
if(fast == NULL||fast->next==NULL)
return NULL;
ListNode * pre=head;
ListNode*cur=head->next;
while(cur!=NULL){
if(pre==cur)
return pre;
pre->next=NULL;
pre=cur;
cur=cur->next;
}
return pre;
}
public class Solution { /* * 思路: * 1)首先判断是否有环,有环时,返回相遇的节点,无环,返回null * 2)有环的情况下,求链表的入环节点 * fast再次从头出发,每次走一步,slow从相遇点出发,每次走一步,再次相遇即为环入口点。 */ public ListNode detectCycle(ListNode head) { if(head == null) return null; ListNode slow = head; ListNode fast = head; ListNode meetNode = null; while(fast!=null && fast.next!=null) { slow = slow.next; fast = fast.next.next; if(slow == fast) { //快慢指针相遇,说明有环,记录相遇节点,跳出循环 meetNode = slow; break; } } if(meetNode == null) return null; //相遇节点为空,说明无环,返回null fast = head; //fast再次从头出发 while(slow != fast) { //再次相遇即为环入口点 slow = slow.next; //slow每次走一步 fast = fast.next; //fast每次走一步 } return slow; } }
/** 思路: 1.还是先用快慢指针方法,找出快慢指针相遇的点; 2.重新定义两个指针,一个为head,另一个为快慢指针相遇点; 3.两个指针每次走一步,相遇点则是链表环的起点; */ public class Solution { public ListNode detectCycle(ListNode head) { if(head == null){ return null; } ListNode slow = head; ListNode fast = head; while(fast != null && fast.next != null){ slow = slow.next; fast = fast.next.next; if(slow == fast){ ListNode node1 = head; ListNode node2 = fast; while(node1 != node2){ node1 = node1.next; node2 = node2.next; } return node1; } } return null; } }
class Solution {public:ListNode *detectCycle(ListNode *head) {if(!head)return nullptr;if(!head->next)return nullptr;ListNode *pSlow = head;ListNode *pSlow2= head;ListNode *pFast = head;while(pFast&&pFast->next){pSlow = pSlow->next;pFast = pFast->next->next;if(pFast==pSlow)break;}if(!pFast||!pFast->next){//reach endreturn nullptr;}else{//loopwhile(pSlow2 != pSlow){pSlow2 = pSlow2->next;pSlow = pSlow->next;}return pSlow2;}}};
ListNode *detectCycle(ListNode *h) { ListNode *p = h, *q = h; while (q && q->next) { p = p->next; q = q->next->next; if (p == q) { while (h != q) { q = q->next; h = h->next; } return h; } } return NULL; }
# class ListNode: # def __init__(self, x): # self.val = x # self.next = None # # # @param head ListNode类 # @return ListNode类 # class Solution: def detectCycle(self , head ): # write code here if not head: return None slow = head fast = head while fast and fast.next: slow = slow.next fast = fast.next.next if slow == fast: node1 = head node2 = slow while node1 != node2: node1 = node1.next node2 = node2.next return node1 return None
python版:
class Solution: def detectCycle(self , head ): if not head: return None slow = fast = head while fast and fast.next: slow = slow.next fast = fast.next.next if slow is fast: slow = head while slow is not fast: slow = slow.next fast = fast.next return fast return None
class Solution { public: ListNode *detectCycle(ListNode *head) { if(head == NULL) return NULL; ListNode *p = head, *q = head; while(q && q->next) { p = p->next; q = q->next->next; if(p == q) { while(q != head) { q = q->next; head = head->next; } return head; } } return NULL; } };
public class Solution { public ListNode detectCycle(ListNode head) { ListNode p=head,q=head; if(p==null) return null; while(true){ if(p.next==null) return null; else if(p.next.next==null) return null; p=p.next.next; q=q.next; if(p==q) break; } if(head.next==head) return head; for(q=head,p=head.next;p!=null;p=p.next){ q.next=null; q=p; } return q; } }
/** * Definition for singly-linked list. * class ListNode { * int val; * ListNode next; * ListNode(int x) { * val = x; * next = null; * } * } */ public class Solution { public ListNode detectCycle(ListNode head) { if(head == null) return null; ListNode fast = head; ListNode slow = head; boolean isRing = false; while(fast != null && fast.next != null){ slow = slow.next; fast = fast.next.next; if(fast == slow){ isRing = true; break; } } if(!isRing) return null; fast = head; while(fast != slow){ fast = fast.next; slow = slow.next; } return fast; } }
环儿的入口结点:从head起,第一个在环儿里的结点。 思路:爱的暴力转圈圈,时间复杂度O((n-m)*m),n=链表长度,m=环的长度;最坏接近O(n^2) 1)定义一对快慢指针,先让两个指针进入环中。 若指针走的过程中转角遇到空null结点,说明没有环,return null即可。唉!没找到爱的圈圈。 2)两个指针进入环中相遇后,进入下一步找环入口操作: 从head依次往后枚举判断当前枚举的结点是否在环中,判断方法: 让环中指针在环内转圈,若途中遇到了当前枚举的结点,说明该结点在环中。 找到第一个这样的结点就是入口结点。
public ListNode detectCycle(ListNode head) { if(head!=null){ ListNode first=head,second=head.next,start;//定义指针 //尝试把指针拉入环中 while(first!=second){ if(first==null||second==null||second.next==null) return null;//没环 first=first.next; second=second.next.next; } //有环 //找环入口:从head起依次往后枚举,第一个在环里的结点就是环的入口结点 start=second;//标记环起点 first=head; while(first!=null){ //second指针在环里转一圈,查看当前结点first是否在环里 do{ if(second==first) return first;//找到的第一个就是结果 second=second.next; }while(second!=start);//转一圈退出 first=first.next;//枚举下一个 } return null; } return null; }
//思路:和判断是否有环解法一致,将遍历后的节点指向一个新增节点,如果遍历过程中,某个节点指向了那个节点,那么这个节点就是入口节点(即交叉点)。
public static ListNode detectCycle(ListNode head) { ListNode target = new ListNode(-1); ListNode cur = head; while(cur != null) { if(cur.next == target) { return cur; } ListNode next = cur.next; cur.next = target; cur = next; } return null; }
public class Solution { public ListNode detectCycle(ListNode head) { /* 快慢指针,第一次相遇快指针走过的是慢指针的两倍 * head到环起始位置为l, 环周长为r, 第一次相遇点在环起点后x * s = l + x; 2s = l + r + x; => l + x = r; => l = r - x; * 同时从head和第一次相遇点往后走,相遇位置即为环起点 */ ListNode fast = head; ListNode slow = head; while(fast!=null&&fast.next!=null){ fast = fast.next.next; slow = slow.next; if(fast == slow){ break; } } if(fast==null||fast.next==null) return null; slow = head; while(slow != fast){ slow = slow.next; fast = fast.next; } return slow; } }
/** 一个新思路,可能没有满足不使用额外空间的要求,不过使用的也不多 毕竟大多数牛友所述的Floyd判圆算法不是第一次就能够轻易想出来的 1. 先利用快慢指针判断是否存在环,并在这个过程中对快指针走过的结点数计数,假设为2*count 2. 从链表中第一个结点开始指定,依次让慢指针走2*count步,如果没有与指定结点相遇,就让指定结点向后移动 3. 当第一次出现慢指针与指定结点相遇的情况时,该结点即为环入口,返回该结点即可 */ public class Solution { public ListNode detectCycle(ListNode head) { ListNode onestep=head; ListNode twostep=head; ListNode p=head; long count=0; while(twostep!=null && twostep.next!=null){ onestep=onestep.next; twostep=twostep.next.next; count++; if(onestep==twostep){ while(true){ for(int i=0;i<count*2;i++){ onestep=onestep.next; if(onestep==p){ return p; } } p=p.next; } } } return null; } }