LCP142 环形链表
先上结果
前排提醒,本文有两种解法,和原理分析
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
这道题真是让人思绪万千,有百般解法
- 最笨的方法是,历遍,每过一个节点就存储起来,每到一个节点再对比。time:O(n*n) space:O(n)
- 既然涉及对比,首先想到使用hash表来优化查找操作,hash的查找是O(1)所以time:O(n),space:O(n)
- 最好的方法就是floyd寻圈算法,记录在经典算法这一节中.time:O(n),space:O(1)
- 不需要额外的存储空间,只需要两个指针
hash的answer
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
unordered_set<ListNode *> visited;
while (head != nullptr) {
if (visited.count(head)) {
//uset.count 此函数接受单个参数element 。表示容器中是否存在需要检查的元素。
return head;
}
visited.insert(head);
head = head->next;
}
return nullptr;
}
};
floyd的算法
原理分析
-
在起点处放置两个指针,一快一慢。快指针每次走两个,慢指针每次一个。
-
循环该步骤,若无环,则到结尾也不会相遇。若存在一个环,那快慢指针将先后进入环中,而速度不同,必然追及,这是一个重要的结论:他们必然相遇在环上
-
而且相遇的时候,循环的次数=慢指针走过的路程=环的长度,图示法如下
- -
好的,现在我们已经求得**是否有环?和环多长?**的问题,下面让我们研究一下环的入口在哪里
-
也很简单,把一个指针移到链表的开头,另一个指针保持在原地,然后让两个指针的速度都是1,最后两者必然相遇,相遇的点必然是环的开头,同理使用图解法
-
相遇点到开头的长度=环的长度,既是AP=⚪P 今同减去路程BP,即为重置的指针路程相同,而速度一致,必然有同时到达环之开头B之事实
-
这就是精妙的Floyd判圈
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast=head, *slow=head;
do{
//如果fast或者下一个元素为null 则必为直线型
if(!fast|| !fast->next) return nullptr;
fast=fast->next->next;
slow=slow->next;
}while(fast!=slow);
//相遇之后,必然存在节点
fast=head;
while(fast!=slow)
{
slow=slow->next;
fast=fast->next;
}
return fast;
}
};