环形链表 II : 找入环口,并返回入环口结点 (环形链表经典题)

力扣原题链接

题目:

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。

通用思路:

定义快慢指针 slow 和 fast,slow 一次走 1 步, fast 一次走 2 步。

遍历链表,直到 slow 和 fast 相遇停止。在相遇处定义一个指针 meet。

head 与 meet 同时移动,每次移动一步,直到两者相遇,停止移动。

根据公式推导,可得结论:head 与 meet 相遇处,即是入环口。

思路草图如下:

 代码如下:

//核心代码段实现

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    struct ListNode* meet = NULL;

    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;

        if(slow == fast)
        {
            //记录slow 与 fast 第一次相遇时的结点
            meet = slow;

            //再次遍历链表,直到 head 与 meet 相遇
            while(head != meet)
            {
                head = head->next;
                meet = meet->next;
            }
            return meet;
        }
    }
    return NULL;
}

另一种思路:

在 slow 与 fast 第一次相遇时,记录下一个结点,并时相遇结点的 next 为空。即将找入环口的问题转化为 两链表相交,求交点的问题。

步骤可分为:

1、找快慢指针第一次相遇的结点

2、判断链表是否有环

3、如果有,则求两链表的长度,并比较两链表长度,计算其长度差,让长链表先走两者的距离步(即两链表长度差为多少,就先让那个长的链表先走多少步,之后两链表在一起移动,直到相遇,相遇代表结点一样)

4、找第一个相交点,即入环口。

 代码如下:

//核心代码段:

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    struct ListNode* meet = NULL;
    struct ListNode* newhead = NULL;

    //用来判断是否有环,如果有,则flag会被改变
    int flag = -1;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;

        //找第一次相遇的结点
        if(slow == fast)
        {
            meet = slow;
            //将环断开,分为两段链表,通过两端链表找相交点,即入环口
            newhead = meet->next;
            meet->next = NULL;
            flag = 1;
            break;
        }
    }
    
    //如果flag 没有被改变,则代表该链表无环
    if(flag == -1)
        return NULL;

    
    struct ListNode* ogcur = head;
    struct ListNode* newcur = newhead;
    int oglen = 1;
    int newlen = 1;

    //求两链表长度
    while(ogcur->next)
    {
        ogcur = ogcur->next;
        oglen++;
    }
    while(newcur->next)
    {
        newcur = newcur->next;
        newlen++;
    }

    //比较两链表长度
    struct ListNode* shortList = head, *longList = newhead;
    if(oglen > newlen)
    {
        shortList = newhead;
        longList = head;
    }

    //长链表先走两者的距离步
    int gap = fabs(oglen - newlen);
    while(gap--)
    {
        longList = longList->next;
    }

    //找第一个相交点,即入环口
    while(shortList != longList)
    {
        shortList = shortList->next;
        longList = longList->next;
    }
    return shortList;
}

创作不易,希望看完的朋友们,如果对你们有帮助,给点支持!谢谢各位。

属于初学者,如果有哪里说的不好,欢迎各位大佬指点!

  • 14
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值