142. 环形链表 II
- 1. 题目描述
- 2.详细题解
- 3.代码实现
- 3.1 Python
- 哈希表
- 快慢指针
- 3.2 Java
- 哈希表
- 快慢指针
1. 题目描述
题目中转:142. 环形链表 II
2.详细题解
判断链表是否形成了环路,非常常规和直观的思路是,依次遍历每个结点并记录下来,当再次遍历到此前遇到过的结点,那么就可以判断存在环路,借助哈希表可以很方便的实现.
该题进阶要求常量内存空间,哈希表方法虽然简单但需要使用额外的内容空间,那么该如何进一步解决呢?在现实生活中,两个人同时同地同向跑步,当两人再次相遇的时候,跑得块的人一定比跑得慢的人多跑的距离是整数圈。同理,如果是链表中有环,那么如果两个指针的速度不一致,那么这二者终将相遇,因此可以使用双指针,一个慢指针,一个快指针实现链表是否有环的判断:如果有环,二者终将相遇,如果没有环,快指针先到链尾,为便于计算,快慢指针速度为2:1。
假设链表环外的长度为A,环的长度为B,第一次相遇时距离入环点的距离为x,那么此时慢指针走过的距离为:A+x,快指针假定已绕环n圈(n>=1),快指针距离为:A+nB+x;快指针走过的距离为慢指针的2班,因此有等量关系:A+nB+x = 2(A+x),可以化简为:A=nB-x,由此可见,外环的距离等于n圈环长减去第一次距离入环点的距离。当快慢指针第一次相遇时,同时增加第三个指针再从头开始移动到入环点,移动距离为A,此时慢指针走过的距离为nB-x;第三个指针开始移动时,为慢指针的位置是在第一次相遇点(距离入环处距离为x),第三个指针到入环点时,此时慢指针恰好也同时回到入环点(第三个指针走A,慢指针走了A=nB-x),慢指针和第三个指针将再次相遇,因此可以确定出入环位置。当快慢指针第一次相遇时,不再需要快指针了,因此可以借助快指针充当第三个指针,每次移动距离为1.
3.代码实现
3.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]:
existed = set()
ptr = head
while ptr:
if ptr in existed:
return ptr
existed.add(ptr)
ptr = ptr.next
return None
快慢指针
# 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]:
slow, fast = head, head
while fast is not None:
slow = slow.next
if fast.next is None:
return None
fast = fast.next.next
if fast == slow:
fast = head
while fast != slow:
fast = fast.next
slow = slow.next
return slow
3.2 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) {
Set<ListNode> existed = new HashSet<>();
ListNode ptr = head;
while (ptr != null) {
if (existed.contains(ptr)) {
return ptr;
}
existed.add(ptr);
ptr = ptr.next;
}
return null;
}
}
快慢指针
/**
* 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) {
ListNode slow = head, fast = head;
while (fast != null){
slow = slow.next;
if (fast.next == null){
return null;
}
fast = fast.next.next;
if (fast == slow){
fast = head;
while (fast != slow){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
return null;
}
}
执行用时不必过于纠结,对比可以发现,对于python和java完全相同的编写,java的时间一般是优于python的;至于编写的代码的执行用时击败多少对手,执行用时和网络环境、当前提交代码人数等均有关系,可以尝试完全相同的代码多次执行用时也不是完全相同,只要确保自己代码的算法时间复杂度满足相应要求即可,也可以通过点击分布图查看其它coder的code