【剑指Offer】面试题56:链表中环的入口结点

整理自剑指Offer


一:题目描述


一个链表中包含环,请找出该链表的环的入口结点。


二:解题思路


链表中有环,情况如图所示:

可以用两个指针解决这个问题

第一步:指针p1,p2指向链表的头结点。

第二步:如果结点中的环有n个结点,指针p1先在链表上向前移动n步,然后两个指针以相同的速度向前移动

第三步:当第二个指针指向环的入口时,第一个指针已经围绕环走了一圈又回到入口结点(即p1=p2)

过程如下图:



现在要解决,如何获得环中结点数目。如果我们能获得环内的一个结点,那么从该节点出发回到该节点的长度就是环的长度。那么问题又变成求环内的一个结点。

可以采用现实生活中,跑步扣圈的思想,如果一个跑的快,一个跑的慢,那么快的一定会在一定时间追上慢的。

利用一快一慢的指针,慢指针每次走一步,快指针每次走两步,如果链表中存在环,则快慢指针相等的结点,一定就在环内。

如下图:

梳理一下思路:

1.找到环内的任意一个结点(采用一快一慢指针)

2.根据环内的结点,求出环的长度

3.利用两个指针,一个指针先走环的长度,然后两个指针一起走,相等的结点即是环的入口。


三:代码实现

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    
    //找链表环中一个结点,无环则返回空
    ListNode* NodeInHoop(ListNode* pHead){
        
        //边界条件
        if(pHead==NULL)  
            return NULL;
        
        //链表不空,两种情况 1有环 2无环
        //利用一快一慢指针,快的指针走两步,慢的指针走1步
        ListNode* pSlow=pHead->next;
        if(pSlow==NULL){
            return NULL; 
        }
        ListNode* pFast=pSlow->next;
        //有环就不会指向NULL
        while(pFast!=NULL && pSlow!=NULL){
            //如果两个指针相等,链表有环,两指针指向环中的一个结点
            if(pFast==pSlow)
                return pFast;
            //不相等,慢指针移动1位,快指针移动2位
            pSlow=pSlow->next;
            
            pFast=pFast->next;
            if(pFast!=NULL)
                pFast=pFast->next;
        }
        //无环,最终的next会指向NULL
        return NULL;
    }
    
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        //边界条件
		if(pHead==NULL)
            return NULL;
        
        //查找环中的一个结点
        ListNode* pNodeInHoop=NodeInHoop(pHead);
        //无环
        if(pNodeInHoop==NULL)
            return NULL;
        //有环
        //计算环的长度:以该结点开始遍历,回到该结点,获得环的长度
        ListNode* pNode1=pNodeInHoop;
        int lenHoop=1;
        while(pNode1->next!=pNodeInHoop){
            lenHoop++;
            pNode1=pNode1->next;
        }
        
        ListNode* pNode2=pHead;
        pNode1=pHead;
        //将pNode1先移动lenHoop
        for(int i=1;i<=lenHoop;i++){
            pNode1=pNode1->next;
        }
        
        while(pNode1!=pNode2){
            pNode1=pNode1->next;
            pNode2=pNode2->next;
        }
        return pNode1;
        
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值