剑指offer23:链表中环的入口节点

        题目:如果一个链表中包含环,如何找出环的入口节点?例如,在下图所示的链表中,环的入口节点是节点3。

    (1)判断一个链表是否包含环?
        
可以用两个指针来解决这个问题,定义一个慢指针和一个快指针,这两个指针同时从链表的头节点出发,慢指针一次走一步,快指针一次走两步,如果快指针追上了慢指针,说明链表就包含环,如果快指针走到了链表的末尾都么追上慢指针,说明链表不包含环。
    
(2)如何找到环的入口?
        
定义两个指针p1和p2指向链表的头节点,如果链表中的环有n个节点,则让指针p1先在链表上向前移动n步,然后两个指针以相同的速度向前移动,当指针p2指向环的入口节点时,指针p1已经围绕着环走了一圈,又回到了入口节点。
    (3)如何得到环中节点的数目?
        在(1)中提到判断一个链表是否包含环时用到了快慢指针,如果两个指针相遇,说明链表中存在环,两个指针相遇的节点一定是在环中,因此可以从这个节点出发,一边继续向前移动一边计数,当在次回到这个节点时,就可以计算出环中的节点数。

        以题目中的实例为例,分析两个指针的移动规律:
    (1)指针p1和指针p2在初始化时都指向链表的第一个节点,如图1所示;
    (2)由于次环中有4个节点,所以指针p1先在链表上向前移动4步,如图2所示;
    (3)接下来两个指针以相同的速度在链表上向前移动,直到它们相遇,它们相遇的结点正好是环的入口节点,如图3所示。

具体代码如下所示 :

#include <iostream>
#include <assert.h>
using namespace std;

struct ListNode
{
	int value;
	ListNode* next;
};

void Init(ListNode* pListHead)
{
	assert(pListHead != NULL);
	if(pListHead == NULL)
	{
		return ;
	}
	pListHead->next=NULL;
}

static ListNode* Buynode()
{
	ListNode *pnewnode=new ListNode();
	assert(pnewnode != NULL);
	pnewnode->next=NULL;
	return pnewnode;
}

bool InsertTail(ListNode *pListHead,int val)
{
	if(pListHead == NULL)
	{
		return false;
	}
	ListNode *pCur=pListHead;
	while(pCur->next != NULL)
	{
		pCur=pCur->next;
	}

	ListNode *pnewnode=Buynode();
	pnewnode->value=val;
	pCur->next=pnewnode;
	return true;
}

ListNode* MeetingNode(ListNode* pListHead) //判断链表是否存在环,若存在返回环中的一个节点,若不存在环,返回NULL
{
	if(pListHead == NULL)
	{
		return NULL;
	}
	ListNode *pslow=pListHead->next;
	if(pslow == NULL)
	{
		return NULL;
	}

	ListNode* pfast=pslow->next;
	while(pfast != NULL && pslow != NULL)
	{
		if(pfast == pslow)
		{
			return pfast;
		}
		pslow=pslow->next;
		pfast=pfast->next;
		if(pfast->next != NULL)
		{
			pfast=pfast->next;
		}
	}
	return NULL;
}

ListNode *EntryNodeOfLoop(ListNode* pListHead)  //找环的入口节点
{
	ListNode *meetingNode=MeetingNode(pListHead);
	if(meetingNode == NULL)  //链表不存在环
	{
		return NULL;
	}

	int nodeInLoop=1;
	ListNode* pNode1=meetingNode;
	while(pNode1->next != meetingNode)
	{
		pNode1=pNode1->next;
		++nodeInLoop;  //计算环中的结点数目
	}

	pNode1=pListHead;
	for(int i=0;i<nodeInLoop;++i)   //先让pNode1指针先走nodeInLoop步
	{
		pNode1=pNode1->next;
	}

	ListNode* pNode2=pListHead;
	while(pNode1 != pNode2)
	{
		pNode1=pNode1->next;
		pNode2=pNode2->next;
	}
	return pNode1;  //返回环的入口节点
}

int main()
{
	ListNode pListHead;
	Init(&pListHead);
	for(int i=0;i<6;i++)
	{
		InsertTail(&pListHead,i+1);
	}

	ListNode *plast=&pListHead;
	while(plast->next != NULL)
	{
		plast=plast->next;
	}

	ListNode *pfirst=&pListHead;  //pfirst指向头节点
	pfirst=pfirst->next;  //pfirst->value=1;
	pfirst=pfirst->next;  //pfirst->value=2;
	pfirst=pfirst->next;  //pfirst->value=3;
	plast->next=pfirst;   //让最后一个节点指向第三个节点形成环

	ListNode *tmp=EntryNodeOfLoop(&pListHead);
	cout<<tmp->value<<endl;
	return 0;
}

运行结果如下所示:
 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值