Given a linked list, determine if it has a cycle in it.
To represent a cycle in the given linked list, we use an integer pos
which represents the position (0-indexed) in the linked list where tail connects to. If pos
is -1
, then there is no cycle in the linked list.
Example 1:
Input: head = [3,2,0,-4], pos = 1
Output: true
Explanation: There is a cycle in the linked list, where tail connects to the second node.
Example 2:
Input: head = [1,2], pos = 0
Output: true
Explanation: There is a cycle in the linked list, where tail connects to the first node.
Example 3:
Input: head = [1], pos = -1
Output: false
Explanation: There is no cycle in the linked list.
Follow up:
Can you solve it using O(1) (i.e. constant) memory?
--------------------------------------------------------------------------------------------------------------------------------
https://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare
其实很多东西并不难,但是中文资料却几乎没有能说清楚的。
这道题是弗洛伊德龟兔算法:请查看上面的维基百科链接。
先声明几个变量:
u表示环的第1个元素的序号(序号从0开始)
λ表示环的长度
那么当 i>=u时,x[i] = x[i+kλ] 其中k>=0
一定存在某个时候,使得i=kλ (需满足i>=u) ,即x[i] = x[i+kλ] = x[2*i]
(上面这种情形是一定会出现的,很容易想明白)
那么也就是说,只要存在环,就一定存在i,使得x[i] = x[2*i]
那么我们使得龟兔同时从x[0]开始,龟一次走一步,兔一次走两步,则兔所在的节点的序号是龟的两倍。当龟兔相遇时,即x[i] = x[2*i],即可判定有环。此时龟兔所在的节点一定是在环内的。
代码如下:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head){
ListNode hare = head;
ListNode tortoise = head;
while(hare!=null && hare.next!=null){//
hare = hare.next.next;
tortoise = tortoise.next;
if(hare==tortoise) return true;
}
return false;
}
}
判定有环无环问题到这里就结束了,但是如果想知道环的入口的节点序号还需要进行以下操作:
当hare和tortoise相遇时,使:
tortoise从相遇点开始,每次走一步
hare回到链表开头,每次走一步
那么当hare到达环的入口时,hare为x[u], tortoise为x[i+u] 注意hare与tortoise的相遇点的i是满足i=kλ的,且x[i]在环内。
则x[i+u] = x[kλ+u] = x[u],即当hare到达环的入口时,tortoise也到了环的入口。
即此次的相遇点就是环的入口。
在hare从链表开头开始向前走的时候,可以记录步数来得到环的入口位置。
得到环的入口位置后,很容易得到环的长度:
从入口开始走,记录步数,当发现等于入口时即绕了一圈。
-------------------------------------------------------------------------------------------------
更新:
今天发现佛洛依德龟兔法其实就是一个追及问题。
另外,
下面再贴出使用hashset的解法(空间复杂度为O(n))
public boolean hasCycle(ListNode head) {
Set<ListNode> seen = new HashSet<>();
while(head!=null){
if(!seen.add(head)){
return true;
}
head = head.next;
}
return false;
}