1 链表中环的入口结点
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
数据范围: n\le10000n≤10000,1<=结点值<=100001<=结点值<=10000
要求:空间复杂度 O(1),时间复杂度 O(n)
解题思路
【C++解法】
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
1、暴力解法
时间复杂度:o(n);空间复杂度:O(n)。不符合要求。
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead) {
if (pHead == NULL || pHead->next == NULL) {return NULL;}
unordered_set<ListNode*> v;
while(pHead != NULL) {
if(v.count(pHead) > 0) {return pHead;}
v.insert(pHead);
pHead = pHead->next;
}
return NULL;
}
};
2、快慢指针
时间复杂度:o(n);空间复杂度:O(1)。
两个结论:
1、设置快慢指针,假如有环,他们最后一定相遇。
2、两个指针分别从链表头和相遇点继续出发,每次走一步,最后一定相遇与环入口。
证明1:设置快慢指针fast和slow,fast每次走两步,slow每次走一步。假如有环,两者一定会相遇(因为slow一旦进环,可看作fast在后面追赶low的过程,每次两者都接近一步,最后一定能追上)。
证明2:
设:
链表头到环入口长度为--a
环入口到相遇点长度为--b
相遇点到环入口长度为--c
则:相遇时
快指针路程=a+(b+c)m+b
慢指针路程=a+(b+c)n+b
其中b+c为环的长度,m为绕环的圈数(m>=1, n>0, m>n)。
快指针走的路程是慢指针的两倍,所以:
(a+(b+c)n+b)*2=a+(b+c)m+b
化简可得:
a=(m-n)(b+c)+c 这个式子的意思是: 链表头到环入口的距离=相遇点到环入口的距离+(m-n)圈环长度。其中m-n>0圈。所以两个指针分别从链表头和相遇点出发,最后一定相遇于环入口。
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead) {
if (!pHead || !pHead->next) {return nullptr;}
ListNode* fast = pHead;
ListNode* slow = pHead;
while (fast->next && fast->next->next) {
fast = fast->next->next;
slow = slow->next;
if (fast == slow) {
slow = pHead;
while (slow != fast) {
slow = slow->next;
fast = fast->next;
}
return slow;
}
}
return nullptr;
}
3、如果允许修改链表
时间复杂度:o(n);空间复杂度:O(1)。
修改value做flag
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead) {
if (!pHead || !pHead->next) {return nullptr;}
while (pHead != nullptr){
if(pHead->val < 0) {
pHead->val = - pHead->val;
return pHead;
}
pHead->val = - pHead->val;
pHead = pHead->next;
}
return nullptr;
}
};
【Java解法】
1、暴力解法
import java.util.Set;
import java.util.HashSet;
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
if (pHead == null) {return pHead;}
Set<ListNode> vis = new HashSet<>();
while (pHead != null) {
if(vis.contains(pHead)) {return pHead;}
else {vis.add(pHead);}
pHead = pHead.next;
}
return null;
}
}
2、快慢指针
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
if(pHead == null || pHead.next == null) {return null;}
ListNode fast = pHead;
ListNode slow = pHead;
while(fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
if(fast == slow) {
slow = pHead;
while(slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
return null;
}
}
【同款LeetCode问题】
2 相关问题
【判断是否有环】
【1-n间n+1个数字中的重复数字】
找出在[1, n]之间的n+1个数字中的重复数字,要求不能修改数组,也不能使用额外的空间。