当前位置:   article > 正文

单链表环问题的证明_单链表环入口证明

单链表环入口证明

Mar 2, 2016

首先声明一下节点的定义。

struct Node
{
	int val;
	Node* next;
public:
	Node(int v) :val(v), next(NULL)
	{}
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

本文的符号图示如下:
这里写图片描述
1、判断链表是否带环
判断链表是否带环,可以在头结点设两个指针,一个叫fast,一个叫slow,fast一下走两步,而slow一下走一步。如果链表中存在环的话,那么fast和slow必定会在环中相遇。若链表中没有环的话,那么fast必定先于slow指针先到达链表的尾节点。
建立数学模型证明如下:
令环长度为k,环节点编号为0,1,2,…,k-1。首先来看两节点初始都在节点0的时候的情况,时间t的时候两个指针的位置节点分别为 t % k t\%k t%k 2 t % k 2t\%k 2t%k,节点相遇的时候有
t % k = 2 t % k (1) t\%k=2t\%k \tag{1} t%k=2t%k(1)
这个模型肯定有解,比如t等于k的整数倍这个时候正好相遇在起点。现在主要讨论除此之外还会在别的点相遇吗?这时的问题转化为等式2有解吗?
t % k = 2 t % k (2.1) t\%k=2t\%k \tag{2.1} t%k=2t%k(2.1)
0 < t % k < k (2.2) 0<t\%k<k \tag{2.2} 0<t%k<k(2.2)
将(2_1)转化为 t % k = ( t % k + t % k ) % k t\%k=(t\%k+t\%k)\%k t%k=(t%k+t%k)%k可见,等式右边不等于等式左边的,只有在两边都是零的时候才成立。
现在看看两个指针在刚开始的时候不是同时进入环的情况,因此将模型修改为
t % k = ( 2 t % k + b ) % k (3) t\%k=(2t\%k+b)\%k \tag{3} t%k=(2t%k+b)%k(3)
其中 0 ≤ b < k 0\leq b<k 0b<k,表示慢指针进入环的时候快指针所在的位置,这里假设环的入口节点编号为0。对等式3求解可得 ( t + b ) % k = 0 (t+b)\%k=0 (t+b)%k=0。综上可知若存在环,两个指针必定相遇,并且在慢指针到达节点k-b的时候相遇。
代码如下:

class Solution
{
public:
	bool HasCirclaInList(Node * pHead)
	{
		Node* pFast = pHead, *pSlow = pHead;
		while (true)
		{
			if (pFast->next != NULL && pFast->next->next != NULL)
				pFast = pFast->next->next;
			else
				return false;
			pSlow = pSlow->next;
			if (pFast == pSlow)
				return true;
		}
		return NULL;
	}
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

2、求环长
当知道两个指针在环内的交点后,找一个指针在环内转一圈所走的步数即是这个环的长度。
这里有一个问题:可否用第一次相遇以前快指针走的步数减去满指针走的步数呢?答案是否定的,因为满指针进入的时候快指针可能已经转了不止一圈了,并且如果第一次相遇点编号大于k/2的时候,快指针在满指针进入环以后要在第二次赶上满指针的时候才能相遇。

class Solution
{
public:
	int LengthOfCircleInList(Node* pHead)
	{
		Node* pFast = pHead, *pSlow = pHead;
		bool flag = false;
		while (true)
		{
			if (pFast->next != NULL && pFast->next->next != NULL)
				pFast = pFast->next->next;
			else
				break;
			pSlow = pSlow->next;
			if (pFast == pSlow)
			{
				flag = true;
				break;
			}
		}
		if (flag)
		{
			int length = 1;
			Node* pNode = pSlow;
			while (pSlow->next != pNode)
			{
				pSlow = pSlow->next;
				length++;
			}
			return length;
		}
		else
			return 0;
	}
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

3、求环的入口点
求解思路,找到第一次相遇点以后,将pSlow重新指向pHead,同时将pFast速度降为1,等到再次相遇的节点就是环的入口节点。
数学证明如下:
假设相遇点节点编号为c,有
t=a+c
2t=a+c+Nk
所以(a+c)%k=0,即证。

class Solution
{
public:
	Node* EntranceOfCircleInList(Node* pHead)
	{
		Node* pFast = pHead, *pSlow = pHead;
		bool flag = false;
		while (true)
		{
			if (pFast->next != NULL && pFast->next->next != NULL)
				pFast = pFast->next->next;
			else
				break;
			pSlow = pSlow->next;
			if (pFast == pSlow)
			{
				flag = true;
				break;
			}
		}
		if (flag)
		{
			pSlow = pHead;
			while (pSlow != pFast)
			{
				pSlow = pSlow->next;
				pFast = pFast->next;
			}
			return pSlow;
		}
		else
			return NULL;
	}
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/64495
推荐阅读
相关标签
  

闽ICP备14008679号