题目

给定一个链表的头节点head,返回链表开始入环的第一个节点。 如果链表无环,则返回null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:

  1. 输入:head = [3,2,0,-4], pos = 1
  2. 输出:返回索引为 1 的链表节点
  3. 解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:

  • 链表中节点的数目范围在范围 [0, 104]
  • -10^5 <= Node.val <= 10^5
  • pos 的值为 -1 或者链表中的一个有效索引


    进阶:你是否可以使用 O(1) 空间解决此题?

    解题方法

    双指针法

    参考环形跑道相遇问题。
    设定慢指针速度为1 node/term,快指针速度为2 node/term(保证速度差为1一定相遇)。
    设相遇时时间为t,则有以下等式。
    142. 环形链表 II - 图1
    142.jpg
    可以证明的是,相遇时慢指针运动距离未超过链表长度。因此,对上式进行化简得
    142. 环形链表 II - 图3
    因此可以看出,两个指针分别从链表端点和相遇点出发,以同样的速度前进,最终一定在环形链表入口相遇。

  • 时间复杂度:O(N),其中 N 为链表中节点的数目。在最初判断快慢指针是否相遇时,slow 指针走过的距离不会超过链表的总长度;随后寻找入环点时,走过的距离也不会超过链表的总长度。因此,总的执行时间为 O(N)+O(N)=O(N)

  • 空间复杂度:O(1)。我们只使用了 slow,fast,ptr 三个指针。

    class Solution {
    public:
      ListNode *detectCycle(ListNode *head) {
          ListNode* fast = head;
          ListNode* slow = head;
          while(1) {
              if(fast!=NULL && fast->next!=NULL){
                  fast = fast->next->next;
              }
              else    return NULL;
              slow = slow->next;
              if(fast==slow)  break;
          }
          slow = head;
          while(slow!=fast){
              slow = slow->next;
              fast = fast->next;
          }
          return slow;
      }
    };
    

    哈希表法

    设置哈希表对链表节点进行保存,查询环形入口点。

    class Solution {
    public:
      ListNode *detectCycle(ListNode *head) {
          unordered_set<ListNode*> nodes;
          ListNode* cur = head;
          while(cur!=NULL && cur->next!=nullptr) {
              if(nodes.count(cur))   return cur;
              nodes.insert(cur);
              cur = cur->next;
          }
          return NULL;
      }
    };
    
  • 时间复杂度:O(N),其中 N 为链表中节点的数目。我们恰好需要访问链表中的每一个节点。

  • 空间复杂度:O(N),其中 N 为链表中节点的数目。我们需要将链表中的每个节点都保存在哈希表当中。