链表中环问题详解

这个问题在面试中是老生常谈的问题了,而且,解决这类问题的主要方法就是追击法,因此,这篇博客中只讲解这种方法求解环的问题。

这里给出三个链表中环的问题:

  • 判断链表中是否存在环;
  • 如果有环,求出环的入口;
  • 如果有环,求出环长。

这里给出一副图片,所有的讨论都在这图片上进行。

这里写图片描述

问题一:判断链表是否有环

这个问题很好解决,设置两个指针ptr_1, ptr_2在A处,让ptr_1以1的速度往后走(即每次移动一个位置),ptr_2以2的速度往后走,如果链表中存在环,那么ptr_1, ptr_2都会进入环中,ptr_2会以2的速度追击ptr_1,而且ptr_2一定可以追上ptr_1(即ptr_2 == ptr_1);如果链表中不存在环,那么ptr_1ptr_2之中一定有一个等于空指针。

bool judge_the_circle(ListNode *pHead)
{
    ListNode *ptr_1, *ptr_2;
    for (ptr_1 = ptr_2 = nullptr;
        ptr_1 != nullptr && ptr_2->next != nullptr && ptr_1 != ptr_2;
        ptr_1 = ptr_1->next, ptr_2 = ptr_2->next->next) {}
    return ptr_1 != nullptr && ptr_1 == ptr_2;
}

问题二:如果有环,求出环的入口

还是上面那幅图片,问题一中讲到如果有环,ptr_1ptr_2一定会相遇,但是相遇在哪个地方呢?是不是没有思考过这个问题,要想知道相遇在哪个地方,还要从这两个指针的速度入手。

与上个问题一样的做法,只不过这次我要分一下运动的阶段了:

  • 首先,两个指针都从A点出发,当ptr_1走到B点时,ptr_2走到了C点,由于ptr_2的速度是ptr_1的2倍,所以a段长度等于b段;
  • 这个时候,两个点都进入了环中,并且以后会顺时针一直移动,ptr_2在C点以2的速度追击ptr_1ptr_2ptr_1为参照物,那么ptr_2相对于ptr_1的速度为1,也就是说,ptr_2要以1的速度来走完C - D段(b段等于c段)加c段的距离才能与ptr_1相遇,这个时候,ptr_2还是以ptr_1为参照物,我们再换个视角,在ptr_2追击ptr_1的这段时间中,ptr_1走到哪了?ptr_1的速度为1,和ptr_2的相对速度一样,那么ptr_1走的路程就和ptr_2走的相对路程一样,故ptr_1在这段时间从B点走了C - D段加c段的距离,又b段等于c段,所以,两个指针在D点相遇;
  • 通过上面这个阶段知道了相遇点在D点,又a段等于b段,b段等于c段,故a段等于c段, 这个时候,让ptr_2到A点去,以1的速度往后走,ptr_1还在D点,然后两个指针同时走,再次相遇时,相遇点就是B点,也就是环的入口。

上面数学分析的既是算法的原理,也是算法的步骤,因此可以很清楚地写出代码。

ListNode* EntryNodeOfLoop(ListNode* pHead)
{
    ListNode *ptr_1 = pHead, *ptr_2 = pHead;
    if (!(ptr_1 != nullptr && ptr_2 != nullptr && ptr_2->next != nullptr))
        return nullptr;
    do {
        ptr_1 = ptr_1->next;
        ptr_2 = ptr_2->next->next;
    } while (ptr_1 != nullptr && ptr_2 != nullptr &&
        ptr_2->next != nullptr && ptr_1 != ptr_2);
    if (!(ptr_1 != nullptr && ptr_2 != nullptr && ptr_2->next != nullptr))
        return nullptr;
    for (ptr_1 = pHead; ptr_1 != ptr_2; ptr_1 = ptr_1->next, ptr_2 = ptr_2->next) {}
    return ptr_1;
}

问题三:如果有环,求出环长

理解了问题二,这个问题就很简单了,调用问题二的函数,求出环的入口,然后让ptr_1走到环的入口,然后让ptr_1再次走到环的入口,统计下路程,就是环长。

int lengthOfLoop(ListNode* pHead)
{
    ListNode *enterNode = EntryNodeOfLoop(pHead);
    int ret = 0;
    do {
        ++ret;
    } while ((ptr = ptr->next) != enterNode);
    return ret;
}

这类问题慢慢分析就好了,多画图。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值