剑指Offer——环形链表问题(面试延伸证明)

1、环状链表

在这里插入图片描述
这道题的思路是利用快慢指针方法,这个方法在我以前的博客有讲解:
链表的中间节点——方法二
有环则两个指针一定会相遇,没环则不会相遇。


代码如下 :

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
        {
            return true;
        }
    }
        return false;
}

这道题虽然做完了,但是在面试的时候还会问延伸的问题 如:

环状链表延伸问题

问题一、为什么slow走一步,fast走两步,他们一定会在环里面相遇,会不会永远追不上?请证明!
问题二、slow走一步,fast走3步、4步、n步行不行?为什么?请证明!
问题三、求环的入口点!

在这里插入图片描述

问题一:

不会,假设当slow进入环的时候,跟fast的距离是N,当slow走一步,fast就走两步,此时距离为N-1,以此类推,他们每次走一次,距离就会缩小1,最后一定会缩小到0,就会相遇。

问题二:

假设fast一次走 3 步,那么他们每走一次,距离就会缩小 2,如果slow进环时,和fast的距离N为偶数,那么可以追上。如果为奇数,减到最后距离为-1。
在这里插入图片描述

此时距离变成 C(环的大小)-1 这时候要考虑环的大小,如果环的节点数为偶数,C-1就为奇数,还是追不上。如果环的节点数为奇数,那么就可以追上。
总结一下: 环的大小为奇数就能追上。环的大小为偶数时:如果slow进环时,与fast的距离为奇数,就不能追上,一直打圈;如果与fast的距离为偶数,就可以追上

问题三:

因为fast和slow一定会相遇,那么我们可以假设相遇点为meet

在这里插入图片描述

那么我们可以推出以下公式:
slow走的距离: L + X
fast走的距离:L + n* C + X
*n的原因是L的长度不确定,slow走到入口点时fast可能在环中转了好几圈了
而fast的距离时slow的两倍
所以 2*( L + X) = L + n* C + X
化简得:L = N*C - X
我们先不看N,L = C - X 就相当于一个指针从起点走,一个指针从meet沿逆时针方向走,就会在入口处相遇。

2、链表中环的入口节点

在这里插入图片描述

公式法

这个题就是上面的问题三,那么可以直接写代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        //此时相遇
        if(slow == fast)
        {
           struct ListNode* meet = fast;
           while(meet != head)
           {
               meet = meet->next;
               head = head->next;
           }
           return meet;
        }
    }
    return NULL;

}

相交链表法

从meet的下一个节点处开始到入口处为一个链表,
从起点处到入口处为一个链表。这道题就被转化为相交链表问题,我在之前的博客有过讲解:第七题
图片如下,两条链表用红 绿标出:
在这里插入图片描述

代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        {
            struct ListNode* meet = fast;
            //转化成相交链表
            struct ListNode* newhead = meet->next;
            int lenA = 0, lenB = 0;
            struct ListNode* curA = head, *curB = newhead;
            while(curA != meet)
            {
                lenA++;
                curA = curA->next;
            }
            while(curB != meet)
            {
                lenB++;
                curB = curB->next;
            }
            struct ListNode* longlist = head, *shortlist = newhead;
            if(lenA < lenB)
            {
                longlist = newhead;
                shortlist = head;
            }
            //先走的步数
            int gap = abs(lenA - lenB);
            while(gap--)
            {
                longlist = longlist->next;
            }
            while(longlist != shortlist)
            {
                shortlist = shortlist->next;
                longlist = longlist->next;
            }
            return longlist;
        }
    }
    return NULL;

}
  • 21
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 21
    评论
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

命由己造~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值