题目

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回null
为了表示给定链表中的环,我们使用整数pos来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果pos-1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。

说明:不允许修改给定的链表。
进阶:**你是否可以使用 O(1) 空间解决此题?

实现

思路1 Hashset

首先该题与Leetcode第141题“环形链表”非常相似,很明显该题也能像第141题一样使用哈希表记录访问过的节点,直到找到已存在哈希表中的节点,那么该节点就是入环点

  1. class Solution {
  2. public:
  3. ListNode *detectCycle(ListNode *head) {
  4. unordered_set<ListNode *> visited;
  5. while (head != nullptr) {
  6. if (visited.count(head)) {
  7. return head;
  8. }
  9. visited.insert(head);
  10. head = head->next;
  11. }
  12. return nullptr;
  13. }
  14. };

思路2 双指针

该思路是查看官方题解才想到的方法,非常巧妙。
总结来说,也是使用了第141题“环形链表”的快慢指针方法,不过我们可以发现最终快慢指针相遇的地点与链表长度有一定的数学关系。即假设两指针相遇时快指针已绕环走了n圈,那么从相遇点入环点的距离加上n-1圈的环长,恰好等于链表头部入环点的距离。
推导方法就是根据快指针走的距离是满指针的两倍这条关系,列出一个距离的等式。

  1. # Definition for singly-linked list.
  2. # class ListNode:
  3. # def __init__(self, x):
  4. # self.val = x
  5. # self.next = None
  6. class Solution:
  7. def detectCycle(self, head: ListNode) -> ListNode:
  8. # 因为关系是根据快指针走的距离是慢指针两倍推导的
  9. # 所以起始得设为一样的,否则距离关系也不一样
  10. slowPtr = head
  11. fastPtr = head
  12. while fastPtr:
  13. if not fastPtr.next:
  14. return None
  15. # 慢指针前进1步,快指针前进2步
  16. slowPtr = slowPtr.next
  17. fastPtr = fastPtr.next.next
  18. if fastPtr == slowPtr:
  19. tmp = head
  20. while tmp!= slowPtr:
  21. tmp = tmp.next
  22. slowPtr = slowPtr.next
  23. return tmp
  24. return None

时间复杂度:. 在一开始判断快慢指针是否相遇时,slow指针走国的距离不会超过链表总长度;随后寻找入环点时遍历的结点也不会超过链表总长度。
空间复杂度:142. 环形链表 II - 图1.

参考**
https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/huan-xing-lian-biao-ii-by-leetcode-solution/