题目描述
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
进阶:
你是否可以使用 O(1) 空间解决此题?
原题链接: 环形链表II.
解题思路
一、哈希表
依次遍历整个链表,并创建一个哈希表来存储遍历过的节点,当要存入的节点已经存在于哈希表中,返回该节点即可。若遍历到某节点的next节点为null,说明链表没有环,遍历结束,返回null。
该方法最易于想到,但需要额外的存储空间存遍历过的节点。
二、快慢指针
定义两个指针,一个慢指针,一个快指针,慢指针一次走一步,快指针一次走两步,若两个指针相遇了则有环,没有相遇则无环。初始时,慢指针指向head,快指针也指向head。
当快指针的next节点为null或者快指针本身节点为null时,说明该链表没有环,遍历结束,返回null。如果链表有环,那么快慢指针一定会相遇(比如操场套圈,一定会在某个地方相遇),指向同一个节点,当指向同一个节点时,说明该链表有环。
有环的情况:
当慢指针p走过a时(假设此时的p为入环的第一个节点,如图),快指针q走过2a,假设环剩下的为x,从此时开始,慢指针p走过x,q走过2x,正好在距离入环的第一个节点x处两指针相遇。相遇时,慢指针p一次走一步的速度向入环的第一个节点走a(环一共a+x)步,若此时声明一个指针指向head,也以一次一步的速度向入环的第一个节点,距离也为a,则两个指针相遇的点就是入环的第一个节点。
即使a>环的长度也满足该说法,可自行稍加理解。
/**
* 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){
//空链表 无环 返还null
return null;
}
//慢指针
ListNode p = head;
//快指针
ListNode q = head;
//保证不会对空进行操作,避免出现段错误,引发程序崩溃
while(q!=null && q.next!=null){
p = p.next;
q = q.next.next;
if(p == q){
//快慢指针相遇
//将快指针重新赋值,用来找入环的第一个节点
q = head;
while(p != q){
p = p.next;
q = q.next;
}
//直到p==q,退出while循环,找到入环的第一个节点
return p;
}
}
//循环结束,有null,说明无环。
return null;
}
}