本人转码小白,正在学习算法,欢迎各位大佬批评指正。算法思想来自博客代码随想录。
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<int> 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<iostream>
#include<vector>
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<int> 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;
}
};
/*void printList(ListNode* head)
{
ListNode* cur = head;
int i = 10;
while (cur && i)
{
cout << cur->val << " ";
cur = cur->next;
i--;
}
}*/
int main()
{
Solution solution;
ListNode* test = creatList();
ListNode* ans = solution.detectCycle(test);
cout << ans->val << endl;//题目要求输出指针即可,这里输出值方便检查结果
system("pause");
return 0;
}
消化后加了些自己的理解,不算纯纯搬运工吧,就当作自己的学习笔记。
码字不易,小白求带。