关闭

判断单链表是否有环

739人阅读 评论(0) 收藏 举报

链表结构:

struct List
{
	int data;
	List* next;
};


(1)判断单链表是否有环

采用追赶法,设置两个指针p和q,从链表表头开始,p每一步走两个节点,q每一步走一个节点,如果链表有环则p和q必相遇。

代码如下

// 判断链表是否有环,时间复杂度O(n),空间复杂度O(1)
List* hasLoopInList( List* head )
{
	List *p, *q;
	p = q = head;
	
	do 
	{
		if ( p->next != NULL && q->next != NULL )
		{
			p = p->next->next;
		}
		else
		{
			return NULL;
		}
		q = q->next;
	} while ( p != q );

	return p;
}

(2)找到环的起始入口(交叉点)

设链表总长度为L,链表头节点到交叉点的距离为x,环长为y,则L=x+y;

设节点p和q共走了s步相遇,由于p每次走两个节点,q每次走一个节点,因此p共走2s个节点,q共走s个节点;

设p与q相遇时,p共绕环走了n圈(n>=1),q绕环第一圈未走完,q和p相遇点距环其实节点为a=s-x;

则p所走总节点数2s满足:2s=ny+x+a=ny+x+(s-x)=ny+s;

由上式进一步得到:ny=s=a+x   => a=ny-x;

从而相遇点顺着环的方向再经过r=y-a=个节点到达环的起始入口;

由a=ny-x带入r=y-a,得到r=x-(n-1)y,即x=r+(n-1)y;

因此若设置两个指针,一个从链表头节点开始每一步走一个节点,另一个从相遇点开始每一步走一个节点,则两指针必定在环的起始入口相遇。

根据以上分析,实现找出环起始入口的代码为:

// 如果有环,返回环的起始节点,时间复杂度小于O(n),空间复杂度O(1)
List* findStartNodeOfLoopInList( List* head, List* meetNode )
{
	if ( head == NULL || meetNode == NULL ) return NULL;
	
	List* p = head;
	List* q = meetNode;

	while( p != q )
	{
		p = p->next;
		q = q->next;
	}

	return p;
}

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:130598次
    • 积分:1507
    • 等级:
    • 排名:千里之外
    • 原创:28篇
    • 转载:2篇
    • 译文:0篇
    • 评论:29条
    最新评论