剑指offer-面试题56:链表中环的入口结点

本文介绍了一种利用双指针技巧高效找到链表中环入口节点的方法,并详细解释了其背后的数学原理。通过快慢指针相遇确定环内节点,进而求得环的大小并定位入口。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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


       思路:受面试题15的启发,可以用两个指针来解决这个问题。定义p1和p2两个指针,均指向链表的头结点,如果链表中的环有n个结点,指针p1现在链表上向前移动n步,然后两个指针以相同的速度向前移动。当第二个指针指向环的的入口结点时,第一个指针已经围绕着环走了一圈又回到了入口结点。为什么是p1先走n步?书上这里略过没讲,这里证明一下:假设有一个指针p指向链表头结点,设不在环中的结点个数为m,那么指针p走m步即可第一次到达入口结点,例如图中指针先走两步即可到达入口结点。指针继续向后走,再走n步,因为环的最后一个结点指向了入口结点,又回到了入口结点,所以第二次达到入口结点总共需要m+n步,这才有两个指针的时候p1需要先走n步。

       现在关键的问题是求出环的结点个数,是想一下,如果我们的结点已经在环中,那么走一圈还会回到刚开始的结点,这样就可以统计环中结点的个数。问题又转化为找出任意一个环中的结点。环形链表有什么性质?那就是如果定义两个指针,一个指针每次走两步,另一个指针每次走一步,最终它们会再次相遇,所以相遇的那个结点就是我们要找的环内的结点,问题解决!

ListNode* MeetingNode(ListNode* pHead)
{
    if(pHead == NULL)
        return NULL;
        
    ListNode* p1 = pHead;
    ListNode* p2 = pHead;
    do
    {
        p1 = p1->m_pNext;
        p2 = p2->m_pNext;
        if(p2 != NULL)
            p2 = p2->m_pNext;
        if(p1 == p2)
            return p1;
    }
    while(p1 != NULL && p2 != NULL);
    
    return NULL;
}


ListNode* EntryNodeOfLoop(ListNode* pHead)
{
    if(pHead == NULL)
        return NULL;
        
    ListNode* nodeInLoop = MeetingNode(pHead);
    int sizeOfLoop = 1;
    if(nodeInLoop)
    {
        ListNode* pNode = nodeInLoop->m_pNext;
        while(pNode != nodeInLoop)
        {
            pNode = pNode->m_pNext;
            ++sizeOfLoop;
        }
        
        ListNode* p1 = pHead;
        ListNode* p2 = pHead;
        for(int i = 0; i < sizeOfLoop; ++i)
            p1 = p1->m_pNext;
            
        while(p1 != p2)
        {
            p1 = p1->m_pNext;
            p2 = p2->m_pNext;
        }
        return p1;
    }
    
    return NULL;
}

解法二:

ListNode* CircleStart(ListNode* pHead)
{
	if(pHead == NULL)
		return NULL;
		
	ListNode* fastPointer, *slowPointer;
	fastPointer = slowPointer = pHead;
	int CircleNum = 0;
	do
	{
		fastPointer = fastPointer->m_pNext->m_pNext;
		slowPointer = slowPointer->m_pNext;
		++CircleNum;
		if(fastPointer == NULL || fastPointer->m_pNext == NULL)
			return NULL;
	}
	while(fastPointer != slowPointer);
	
	slowPointer = pHead;
	while(fastPointer != slowPointer)
	{
		fastPointer = fastPointer->m_pNext;
		slowPointer = slowPointer->m_pNext;
	}
	
	return fastPointer;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值