链表中是否有环及环的入口求解

链表常见操作

参考:https://blog.csdn.net/maybe3is3u5/article/details/52276623

https://blog.csdn.net/Lily_whl/article/details/71662133

1.链表中是否有环

参考https://blog.csdn.net/fynjy/article/details/47440049

设置两个指针pFast和pSlow,初始时让他们都指向首节点(第一个元素),这里指向头结点也可以,只不过是为了下面计算环入口方便。让pFast指针每次前进两步,pSlow指针每次前进一步,我们可以想象,这两个指针一定会在环中的某个位置相遇。

//判断链表中是否有环,是返回true,没有返回false
//思路:设置两个指针pFast和pSlow,初始时让他们都指向首节点(第一个元素),
//这里指向头结点也可以,只不过是为了下面计算环入口方便。
//让pFast指针每次前进两步,pSlow指针每次前进一步,我们可以想象,
//这两个指针一定会在环中的某个位置相遇。

bool DetectCricle(PNode pHead)
{
	//指向首节点
	PNode pFast = pHead->next;
	PNode pSlow = pHead->next;
	//链表空
	if (!pFast)
		return false;
	while (pFast->next && pFast->next->next)
	{
		pFast = pFast->next->next;
		pSlow = pSlow->next;
		if (pFast == pSlow)
			return true;
	}
	return false;
}

2.求环的入口

示意图如图所示,当pFast和pSlow第一次相遇时,pSlow一定没有遍历完整个链表,顶多在入口的前一个节点就会相遇,图中就是蓝色的那个点。原因何在?

我们知道两个指针相遇的地方一定在环中的某个点,从入口到蓝色点一圈中的某个位置,假设此时pSlow第一次进入环,也就是刚好指向入口,此时pFast的位置可以指向环中任一位置。我们想象成pFast追赶pSlow的状态,当pFast指向黄色节点时,需要追赶的步数最远,这时一步步走,当pSlow指向绿色节点时,pFast差一步就刚好走满两圈,此时刚好也指向绿色节点,正好相遇(不管在中途有没有相遇)。所以pSlow还没走完一遍,两个节点就会相遇。

设首节点到入口的距离为x,入口到相遇点距离为y,环的长度为r,链表的长度为l,相遇时pSlow走的距离为s,相遇时pFast走了n次环。

我们有:

2s = s+nr;

x+y =s;

得x+y = nr;

的x = nr-y;

我们看看x=nr-y是什么意思,x表示从守节点走到入口的距离,nr-y表示从相遇点开始循环n圈环,再导入y步,刚好又位于入口。此时又相遇了,可以计算出入口位置。

//求环的入口,存在则返回入口地址,不存在返回NULL
PNode CricleEnterAddr(PNode pHead)
{
	//指向首节点
	PNode pFast = pHead->next;
	PNode pSlow = pHead->next;
	//链表空
	if (!pFast)
		return NULL;
	while (pFast->next && pFast->next->next)
	{
		pFast = pFast->next->next;
		pSlow = pSlow->next;
		if (pFast == pSlow)
		{
			pFast = pHead->next;
			while (pFast != pSlow)
			{
				pFast = pFast->next;
				pSlow = pSlow->next;
			}
			return pFast;
		}
	}
	return NULL;
}

3.链表反转

方法一、

思路:每次都将原第一个结点之后的那个结点放在新的表头后面。

比如1, 2, 3, 4, 5

第一次:把第一个结点1后边的结点2放到新表头后面,变成2, 1, 3, 4, 5
第二次:把第一个结点1后边的结点3放到新表头后面,变成3, 2, 1, 4, 5
……

直到: 第一个结点1,后边没有结点为止。

//反转链表
//思路:每次都将原第一个结点之后的那个结点放在新的表头后面。
//比如1, 2, 3, 4, 5
//第一次:把第一个结点1后边的结点2放到新表头后面,变成2, 1, 3, 4, 5
//第二次:把第一个结点1后边的结点3放到新表头后面,变成3, 2, 1, 4, 5
//……
//直到: 第一个结点1,后边没有结点为止。
PNode ListReverse(PNode pHead)
{
	//链表只有头节点,或者只有一个节点
	if (pHead->next == NULL || pHead->next->next == NULL)
	{
		return pHead;
	}
	PNode tail = pHead->next;//第一个节点为尾
	PNode current = tail->next;//current为当前需要调换到前面的节点
	while (current)
	{
		//指向当前节点的下一个节点
		tail->next = current->next;
		//头插法,插入节点
		current->next = pHead->next;
		pHead->next = current;
		//更新当前节点
		current = tail->next;
	}
	return pHead;
}

方法二、

利用三个辅助指针,将链表中元素一个一个反转

//反转链表方法二、利用三个辅助指针,将链表中元素一个一个反转
PNode ListReverse2(PNode pHead)
{
	//链表为空或者只有一个节点
	if (!pHead->next || !pHead->next->next);
	{
		return pHead;
	}
	PNode pre = pHead->next;
	PNode cur = pre->next;
	PNode next = NULL;
	pre->next = NULL;
	while (cur)
	{
		next = cur->next;
		cur->next = pre;
		pre = cur;
		cur = next;
	}
	pHead->next = pre;
	return pHead;
}

不带头结点的链表反转,

类似方法二、

PNode ListReverse3(PNode pHead)
{
	if (!pHead || !pHead->next)
	{
		return pHead;
	}
	PNode pre = pHead;
	PNode cur = pre->next;
	PNode next = NULL;
	pHead->next = NULL;
	while(cur)
	{
		next = cur->next;
		cur->next = pre;
		pre = cur;
		cur = next;
	}
	pHead = pre;
	return pHead;
}

另一种方法:

PNode ListReverse4(PNode pHead)
{
	if (!pHead || !pHead->next)
	{
		return pHead;
	}
	PNode n = pHead;
	pHead = NULL;
	while (n)
	{
		PNode m = n;
		n = n->next;
		m->next = pHead;
		pHead = m;
	}
	
	return pHead;
}

4.求链表倒数第k个节点

//查找链表倒数第K个节点
//方法一:需遍历两次链表;先求链表的长度len,然后遍历len-k的距离即可查找到单链表的倒数第k个节点
//方法二:遍历一次链表,时间复杂度为O(n);设置两个指针p1、p2,让p2先走k-1步,然后p1、p2再同时走,
//        当p2走到链表尾时,p1所指位置就是所要找的节点
PNode ListSearchReverseKthNode(PNode pHead, int k)
{
	if (pHead->next == NULL || k <= 0)
	{
		return NULL;
	}
	PNode p1 = pHead->next;
	PNode p2 = pHead->next;
	while (--k)
	{
		p2 = p2->next;
		if (p2 == NULL)
			return NULL;
	}
	while (p2->next)
	{
		p2 = p2->next;
		p1 = p1->next;
	}
	return p1;


}

5.求链表的中点

//求链表的中间节点,(设链表长度为n,返回第n/2+1个点)
//思路:设置两个指针,p1,p2,p2每次走两步,p1每次走一步
PNode ListSearchMidthNode(PNode pHead)
{
	
	if (pHead == NULL||pHead->next==NULL)
		return NULL;
	PNode p1 = pHead->next;
	PNode p2 = pHead->next;
	while (p2->next&&p2->next->next)
	{
		p2 = p2->next->next;
		p1 = p1->next;
	}
	if(p2->next)
	{
		p2 = p2->next;
		p1 = p1->next;
	}
	return p1;

}

猜你喜欢

转载自blog.csdn.net/hhhhhyyyyy8/article/details/81011016