LeetCode 142_环形链表II c++本地实现(超详细)

本人转码小白,正在学习算法,欢迎各位大佬批评指正。算法思想来自博客代码随想录。


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.本地实现

  1. 先创一个环形链表 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;
}

消化后加了些自己的理解,不算纯纯搬运工吧,就当作自己的学习笔记。

码字不易,小白求带。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值