算法 · 带环单链表求入环节点

目录

一、题目

思路

证明

代码

二、总结


一、题目

已知一个单链表中存在环,求进入环中的第一个节点。

//struct ListNode
//{
//	int m_nKey;
//	ListNode * m_pNext;
//    ListNode(int key) : m_nkey(key),m_pNext(nullptr) {}
//};

// 已知一个单链表中存在环,求进入环中的第一个节点

ListNode* GetFirstNodeInCircle(ListNode * pHead)
{
    
}

思路

        首先判断是否存在环,若不存在则结束。本题已经说明存在环,故可以不进行这一步操作。我们采用快慢指针进行遍历(快指针每次后移2个节点,慢指针每次后移1个节点),两个指针必会在环内某个节点第一次相遇[1],且入环后的循环次数必然不会超过环内节点个数[2]。再将快指针移至头节点,改为每次后移1个节点,慢指针按原来方式后移,交点必为入环节点[3]。

证明

[1] 两个指针必会在环内某个节点第一次相遇

        1. 首先快指针必然先入环,所以若存在相遇,必然与慢指针在环内相遇

        2. 由于在环内相遇,慢指针必入环

        3. 慢指针入环后可以转换为“小学二年级”的追及问题(起始路程为快指针慢指针的节点数,由于  v(快指针) - v(慢指针) = 1(节点/次),所以必然可以追上  <=> 两个指针必会在环内某个节点第一次相遇)

  

[2] 入环后的循环次数必然不会超过环内节点个数

        1. 根据 [1] 的结论,我们可以计算入环后的循环次数。我们选取最差情况(上图即为最差情况的示例),即快指针在慢指针的后一节点,此时路程差为(环内节点数 - 1) ,

路程差 / 速度差 = 时间(操作数)<= 环内节点数 - 1

        故,入环后的循环次数必然不会超过环内节点个数

[3] 快指针移至头节点,改为每次后移1个节点,慢指针按原来方式后移,交点必为入环节点

        令直链的节点数为m,则慢指针的移动次数也为m,环内节点数为n,入环后第 l 个节点相交 (l < n) [2]中已证。

        分类讨论:m > n 

                慢指针和快指针第一次相遇时,慢指针移动操作数为 m + l

                则有 (m + l) * 2 = m + l + kn ①

                我们采用反证法,当操作完成后,慢指针操作数为 m + l + m

                将①代入  有m + l + m = m + kn

                刚好为入环点

                        m <= n                

                慢指针和快指针第一次相遇时,慢指针移动操作数为 m + l

                则有 (m + l) * 2 = m + l + n ①

                我们采用反证法,当操作完成后,慢指针操作数为 m + l + m

                将①代入  有m + l + m = m + n

                也刚好为入环点

        故快指针移至头节点,改为每次后移1个节点,慢指针按原来方式后移,交点必为入环节点。

代码

//已知一个单链表中存在环,求进入环中的第一个节点
ListNode* GetFirstNodeInCircle(ListNode* pHead)
{
	ListNode* fast = pHead, * slow = pHead;
	while (fast != slow) // 寻找环内第一次相遇的节点
	{
		fast = fast->m_pNext->m_pNext;
		slow = slow->m_pNext;
	}
	fast = pHead; // 快指针返回头部
	while (fast != slow) // 相交时为入环节点
	{
		fast = fast->m_pNext;
		slow = slow->m_pNext;
	}
	return slow;
}

二、总结

        双指针 + 数学 + 环链表

猜你喜欢

转载自blog.csdn.net/H_Greddy/article/details/126635901