本人转码小白,正在学习算法,欢迎各位大佬批评指正。算法思想来自博客代码随想录。
1.题目描述:
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
2.题解思路:
- 首先判断是否有环
- 如果有环,找到环起点
2.1 使用快慢指针法判断是否有环
令slow指针和fast指针指向头结点,slow每次走一步,fast每次走两步,如果存在环的话,fast和slow会在环内相遇。
(为什么会相遇?因为对于slow来说,fast是一步一步的靠近slow。我们假设slow不动,只有fast在环内每次走一步,肯定会和slow相遇)
(为什么在环内?因为fast进入环内,就不会出来了)
2.2 公式推一下,找环起点
头结点到环起点的结点数为x ,环起点到相遇点结点数为y,相遇点再到起点结点数为z。
相遇时,slow走过的结点数为(x+y)(后文解释),fast走过的结点数为x+n(y+z)+y。
fast走过的结点数时slow的两倍,所以2*(x+y) = x+n(y+z)+y,变一下形式啦。
x+y = n(y+z)
x = n(y+z) -y
x = (n-1)(y+z) +z
n为圈数,一定是>=1的。当n=1时,x = z,这意味着从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环起点。当n>1时,其实就是fast多转了几圈,结果同理。
为什么相遇时slow走过的结点数为(x+y)?
假如slow和fast都在环起点时, slow转一圈,fast转两圈,结果刚好在入环口相遇。也就意味着最坏的相遇是在入环口,此时走的路最多(slow走了整整一圈)。
然而,当slow在环起点,fast一定是在环的任意位置,所以必定会在slow的第一圈内一点相遇。
举个例子:
下面列举了起始时刻,slow和fast都在1;slow在1fast在2;slow在1fast在3;slow在1fast在4时的4种相遇情况。
3.本地实现
- 先创一个环形链表 3->2->0->-4 -4指向2 形成环
ListNode* creatList()
{
ListNode* head = NULL;
ListNode* cur = NULL;
ListNode* temp = NULL;
vector nums = { 3,2,0,-4 };
for (int i = 0; i < nums.size(); i++)
{
if (head == NULL)
{
head = new ListNode(nums[i]);
cur = head;
}
else
{
cur->next = new ListNode(nums[i]);
if (nums[i] == 0)
{
temp = cur->next; //存一下2,最后把-4指向2
}
cur = cur->next;
}
cur->next = temp;
}
return head;
} 因为是环,输出一下前10位看看结果。
ListNode* creatList()
{
ListNode* head = NULL;
ListNode* cur = NULL;
ListNode* temp = NULL;
vector nums = { 3,2,0,-4 };
for (int i = 0; i < nums.size(); i++)
{
if (head == NULL)
{
head = new ListNode(nums[i]);
cur = head;
}
else
{
cur->next = new ListNode(nums[i]);
if (nums[i] == 0)
{
temp = cur->next; //存一下2,最后把-4指向2
}
cur = cur->next;
}
cur->next = temp;
}
return head;
} 因为是环,输出一下前10位看看结果。
完整代码
#include#include using namespace std; struct ListNode { int val; ListNode* next; ListNode(int x) :val(x), next(NULL) {} }; //先创一个环形链表 3->2->0->-4 -4指向2 形成环 ListNode* creatList() { ListNode* head = NULL; ListNode* cur = NULL; ListNode* temp = NULL; vector nums = { 3,2,0,-4 }; for (int i = 0; i < nums.size(); i++) { if (head == NULL) { head = new ListNode(nums[i]); cur = head; } else { cur->next = new ListNode(nums[i]); if (nums[i] == 0) { temp = cur->next; //存一下2,最后把-4指向2 } cur = cur->next; } cur->next = temp; } return head; } class Solution { public: ListNode* detectCycle(ListNode* head) { ListNode* fast = head; ListNode* slow = head; //开始走 while (fast != NULL && slow != NULL)//有环的话相当于while(1),没环的话fast肯定会走到空值,返回NULL即可。 { slow = slow->next;//slow走一步 fast = fast->next->next;//fast走两步 if (slow == fast)//遇上了,说明有环,开始找环起点。 { ListNode* index1 = fast;//指针1 从相遇点开始走 ListNode* index2 = head;//指针2 从头结点开始走 //两者相遇的位置即为环起点 while (index1 != index2) { index1 = index1->next; index2 = index2->next; } return index1;//环起点 } } return NULL; } }; int main() { Solution solution; ListNode* test = creatList(); ListNode* ans = solution.detectCycle(test); cout << ans->val << endl;//题目要求输出指针即可,这里输出值方便检查结果 system("pause"); return 0; }
消化后加了些自己的理解,不算纯纯搬运工吧,就当作自己的学习笔记。
码字不易,小白求带。



