题目
给定一个链表的头节点head
,返回链表开始入环的第一个节点。 如果链表无环,则返回null
。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
提示:
- 链表中节点的数目范围在范围
[0, 104]
内 -10^5 <= Node.val <= 10^5
pos
的值为-1
或者链表中的一个有效索引解题方法
双指针法
参考环形跑道相遇问题。
设定慢指针速度为1 node/term
,快指针速度为2 node/term
(保证速度差为1
一定相遇)。
设相遇时时间为t
,则有以下等式。
可以证明的是,相遇时慢指针运动距离未超过链表长度。因此,对上式进行化简得
因此可以看出,两个指针分别从链表端点和相遇点出发,以同样的速度前进,最终一定在环形链表入口相遇。时间复杂度:
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
为链表中节点的数目。我们需要将链表中的每个节点都保存在哈希表当中。