《剑指Offer》(23)链表中环的入口节点

本文探讨如何在链表中判断是否存在环并找到环的入口节点。通过使用快慢指针,当快指针追上慢指针时确定存在环,随后计算环内节点数以确定入口节点。

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

#题目:如果一个链表中包含环,如何找出环的入口节点?
链表问题是面试过程中经常会考察到的问题,这个问题其实内含2个小问题:
1.如何判断一个链表中是否包含环;
2.找出带环链表中环的入口点。
当我们用1个指针遍历链表不能解决问题的时候,可以尝试用2个指针来遍历链表。可以让其中一个指针遍历的速度稍快一些(比如:让其中一个指针一次走2步,另一个指针一次走1步),也可以让其中一个指针在链表上先走若干步。
##1.判断一个链表中是否包含环
我们可以定义2个指针,将2个指针都初始化为指向链表的头结点。然后,让第一个指针遍历链表的速度稍快一些即让第一个指针一次走2步,让第二个指针一次走1步。如果快指针能够追赶上慢指针,则说明链表中包含环,此时快指针与慢指针相遇的结点一定在环的内部;如果快指针走到了链表的末尾(快指针指向null)都没有追上慢指针,则说明链表中不包含环。
##2.找到带环链表的环的入口结点
在这一步仍然需要定义2个指针,然后让第一个指针先在链表上走n步(n为环中结点的个数),再让第二个指针从头结点出发,之后两个指针在链表上以相同的速度一次走1步直到两个指针相遇,则相遇的结点就是环的入口结点。
那么我们需要计算环中的结点数n,定义一个计数器,让第一个指针从上一步得到的快指针与慢指针相遇的结点处出发,遍历链表直到回到该相遇结点,此时计数器的值就是环中的结点数。

Java代码如下:

/*
 public class ListNode 
 {
    int val;//原本的数据(数据元素信息)
    ListNode next = null;//下一个节点的位置(后继节点的地址)
    ListNode(int val)//构造函数
    {
        this.val = val;//this关键字用于区分成员变量和局部变量同名的情况
    }
}
*/
public class Solution 
{
    //判断链表中是否包含环。如果包含环的化,返回2个指针相遇的结点(该结点一定在环内);如果不包含环,返回null。
    public ListNode findMeetingNode(ListNode pHead)
    {
        //要时刻注意避免出现空指针异常
        //如果头结点为空,则说明链表为空
        if(pHead == null)
            return null;
        ListNode pSlow = pHead.next;//慢指针从头结点出发,一次走1步
        if(pSlow == null)
            return null;
        ListNode pFast = pSlow.next;//快指针从头结点出发,一次走2步
        while(pFast != null && pSlow != null)
        {
            if(pFast == pSlow)
                return pFast;//存在环
            
            pSlow = pSlow.next;
            pFast = pFast.next;
            if(pFast != null)
                pFast = pFast.next;
        }
        return null;
    }
    //找到环的入口结点
    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        //要时刻注意避免出现空指针异常
        //如果头结点为空,则说明链表为空
        if(pHead == null)
            return null;
        ListNode meetingNode = findMeetingNode(pHead);//上一步中得到的相遇结点
        if(meetingNode == null)
            return null;//表示链表中不包含环
        int count = 1;//计数器,计算环中的结点数
        ListNode tmpNode = meetingNode;//从相遇结点出发
        while(tmpNode.next != meetingNode)
        {
            tmpNode = tmpNode.next;
            count++;
        }
        //让第一个指针先在链表中走count步
        ListNode pNode1 = pHead;
        for(int i = 0;i < count;i++)
        {
            pNode1 = pNode1.next;
        }
        ListNode pNode2 = pHead;
        //让两个指针以相同的速度遍历链表直到相遇
        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、付费专栏及课程。

余额充值