142. 环形链表 II
给定一个链表的头节点 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
解释: 链表中没有环。
方法一:暴力
思路:
1、遍历整个链表,如果当前结点未被记录,将其记录;若有记录(即指向同一结点)返回当前结点;
2、如果遍历完整个链表,只是记录了每个结点,则返回null(即无环)
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
vector<ListNode*> a;
while(head){
for(int i=0;i<a.size();i++){
if(a[i]==head){
return head;
}
}
a.push_back(head);
head=head->next;
}
return NULL;
}
};
方法二:借助哈希容器
思路:同方法一
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
unordered_set<ListNode*> a;
while(head){
if(a.count(head)){
return head;
}
a.insert(head);
head=head->next;
}
return NULL;
}
};
方法三:快慢指针
思路:
1、令fast、slow(快慢指针)指向头结点(fast每次走两步,slow每次走一步),先默认有环
2、第一次相遇(此次相遇一定在环内,因为fast总是会比slow先到环内)
3、第二次相遇(此次相遇在入环点。当在第一次相遇后,根据fast和slow的2倍关系可知,相遇点到入环点的距离加n-1圈的环长,恰好等于链表头到入环点的距离,具体见下方),此时返回第二次相遇点即可
4、若最后fast或fast->next指向空(即无环),返回空
附:设a为head到入环点距离,b为入环点到第一次相遇点的距离,c为第一次相遇点到入环点的距离,则 a+(n-1)b+c=2(a+b)====> a=c+(n-1)(b+c),即最终会在入环点相遇
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *fast=head,*slow=head;
while(fast!=NULL&&fast->next!=NULL){
fast=fast->next->next;
slow=slow->next;
if(slow==fast){
ListNode *per=head;
while(per!=slow){
per=per->next;
slow=slow->next;
}
return per;
}
}
return NULL;
}
};
};