算是链表比较有难度的题目,需要多花点时间理解 确定环和找环入口,建议先看视频。
题目链接/文章讲解/视频讲解:代码随想录
题目:力扣题目链接
206 环形链表II
题目描述
题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
题解
思路
本题主要是考察数学思维,代码并不难。解题要想办法证明:
- 判断链表是否环
- 如果有环,如何找到这个环的入口
1、判断链表是否有环
使用快慢指针,slowIndex和fastIndex,同时从头节点出发,fastIndex一次走两步,slowIndex一次走一步。如果fastIndex == slowIndex(指向同一个节点),则说明有环,并在环中相遇。
因为如果没有环,则有尾节点指向null,fastIndex会先走到最后一个节点(fast.next =null),在fastIndex.next != null 退出循环前,fastIndex 永远不会等于 slowIndex。
2、如果有环,如何找到这个环的入口
在链表有环的前提下,定义两个指针index1和index2,分别指向head 和相交节点(上一步快慢指针相遇的地方)。接着两指针每次一起走一步,直到相等,此时指向的节点就是环的入口,返回index1或index2。
以上方法的推导过程:
2*(x+y) = x+ n(y+z)+z (n为fastIndex在环内转的圈数)
x=(n-1))(y+z) +z
当n=1时,即fastIndex只在环内转了一圈就与slowIndex相遇,有x=z。
说明如果定义两个指针index1和index2,分别指向head 和相交节点(上一步快慢指针相遇的地方)。接着两指针每次一起走一步,直到相等,此时指向的节点就是环的入口。
代码
java版本
/**
* 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) {
//1、判断有无环
ListNode fastIndex=head, slowIndex = head;
while(fastIndex != null && fastIndex.next != null){
slowIndex = slowIndex.next;
fastIndex = fastIndex.next.next;
//2、找环入口节点
if(slowIndex == fastIndex){//相等说明有环,接下来找环入口
ListNode index1 = head; //index1从头节点出发
ListNode index2 = fastIndex; //index2从相遇点出发
while(index1 != index2){//一次一起走一步,相遇点是环入口
index1 = index1.next;
index2 = index2.next;
}
return index2;
}
}
return null;
}
}
复杂度分析
时间复杂度:O(n)
空间复杂度:O(1)
python版本
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
slowIndex,fastIndex = head,head
while fastIndex != None and fastIndex.next !=None:
slowIndex = slowIndex.next
fastIndex = fastIndex.next.next
#指针相等说明有环
if slowIndex == fastIndex:
index1 = head
index2 = slowIndex
while index1 != index2:
index1 = index1.next
index2 = index2.next
return index1
return None