C语言——用快慢指针判断带环的起始位置

上一节讲了快慢指针的基本用法,不熟悉的朋友可以从下面的链接看怎么利用快慢指针判断链表是否有带环http://t.csdnimg.cn/mVNfy

我们设快指针一次走2步,慢指针一次走1步,由上一节我们知道,慢指针和快指针一定会在带环中相遇 ,那么我们这里提供一种思路,具体原理放在文章的结尾。

基本思路:

1.我们在开头定义一个headmove指针指向链表的头节点(为了保证链表的头节点不丢失,我们通过移动headmove来完成我们后面的任务),

2.移动快慢指针

3.判断快指针和慢指针是否相遇,如果相遇了,我们此时定义一个meet指针指向slow和fast相遇位置的节点

4.同时移动meet指针和headmove指针,当他们指向的地址相同时,即为链表带环的起始位置,然后将该节点返回(原理请见下文)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode* fast = head;
	struct ListNode* slow = head;
	struct ListNode* headmove = head;

	while (fast && fast->next)
	{
		fast = fast->next->next;
		slow = slow->next;

		if (fast == slow)//必须在两个指针移动之后再做判断,否则永远返回真
		{
			struct ListNode* meet = slow;
			while (headmove != meet)
			{
				headmove = headmove->next;
				meet = meet->next;
			}
			return meet;
		}
	}
	return NULL;
}

那么问题来了,为什么meet节点指向快慢指针相遇的地方,headmove指向链表开头的地方,两个指针同时移动,相遇的位置即为链表带环的起始位置呢?

我们设带环的长度为c,链表除带环以外的长度为l

C语言——用快慢指针判断带环的起始位置_第1张图片

在快慢指针相交的地方距离带环的起始位置为x

C语言——用快慢指针判断带环的起始位置_第2张图片

我们现在来计算快慢两个指针走过的路程

慢指针:l+x

快指针:l+x+c(这里埋一个伏笔,后面详细解释)(x

插入一段解释,为什么x一定小于c呢?理由如下:假设x=c,那么慢指针走过了一圈,因为快指针是慢指针速度的两倍,所以快指针走过了两圈,由上一节的快慢指针相遇的原理我们知道这种假设不成立,所以x

快指针的速度为慢指针的两倍,由于移动的时间相同,所以快指针走过的总路程是慢指针的两倍则有等式:l+x+c=2(l+x)

                     c=l+x

                   c-l=x

C语言——用快慢指针判断带环的起始位置_第3张图片

l=c-x从上图可以直观看出meet与headmove距离带环起始相同,所以meet和headmove相遇的节点即链表带环的起始节点

但是但是但是但是!!!

C语言——用快慢指针判断带环的起始位置_第4张图片

显然 l=c-x是不成立的,那么是哪里出了问题呢?

这就要提到之前埋的伏笔了,快指针的路程真的是:l+x+c 吗?

如果快指针很早就进入了带环并在慢指针进入带环之前走过了若干圈之后那么快指针的路程应该是:l+x+n*c (n为快指针在慢指针进入带环前快指针走过的圈数)

 所以最后的等式应该是:n*c-x=l  

                                                                                                                                      

   

你可能感兴趣的:(c语言,开发语言,数据结构,链表)