【C语言】环形链表(链表经典带环问题)

一、给定一个链表,判断链表是否带环。

leetcode刷题链接:141. 环形链表 - 力扣(LeetCode)

 

解题思路:(快慢指针法)

①.首先如果一个链表不带环,那么在遍历这个链表,一定会走到NULL

②.如果链表带环,则在遍历这个链表会出现死循环,遍历一个链表通常的方法是用一个指针从头开始向后遍历,很显然用一个指针解决不了这个问题。

③.那么,如果有一点刷题经验的人,会想到,既然一个指针解决不了,那么双指针呢,毕竟在我们的刷题的时候,双指针是一个比较常用的方法,所以这个时候尝试双指针能不能解题。

④.当我们用指针去遍历一个环形链表的时候,无论怎么遍历都遍历不完,就好比如一个人在一个死循环的路里面走,如果是双指针,那就是两个人在死循环里面走,这时重点来了,假设这个死循环就是一个跑道,两个速度不同的人在跑道里走,那么他们肯定会相遇,即两个不同速度的指针在链表里遍历,如果链表带环,则这两个指针会相遇。 

如图所示: 

用一个慢指针从头开始,一个快指针从头的下一个开始,每次慢指针走一步,快指针走两步,如果链表带环,那么这两个指针肯定会相遇,否则快指针会先走到NULL。  

解释:如果链表带环,那么快慢指针都会进入环,在环中,慢指针每次走一步,快指针每次走两步, 从相对运动角度来说,可以看成慢指针不动,快指针每次走一步,那么在环中,快指针一定会遇上慢指针。

思考:如果快指针每次走三步,四部行不行?

           不行。

           因为如果快指针走三步,那么相对慢指针来说,每次走两步,那么就会有可能快遇上的时候,直接越过去。 

leetcode解题代码 

bool hasCycle(struct ListNode *head) 
{
    if(head==NULL || head->next==NULL)//如果链表没有节点或只有一个节点,直接返回。
    {
        return false;
    }    
    
        struct ListNode* slow=head;//慢指针指向头
        struct ListNode* fast=head->next;//快指针指向头的下一个
        while(slow!=fast)//当慢指针不等于快指针
        {
            if(fast==NULL || fast->next==NULL)//如果快指针走到空,则返回假
            {
                return false;
            }
            else
            {
                slow=slow->next;//慢指针走一步
                fast=fast->next->next;//快指针走两步
            }
        }
        return true;
}

二、给定一个链表,如果带环,则返回入环的第一个节点

leetcode刷题链接:142. 环形链表 II - 力扣(LeetCode)

 解题思路:

①.首先要判断链表是否带环,带环才能找第一个入环的结点。判断带环参考第一题。

②.用一个指针从头开始遍历,另一个指针从上题中快慢指针相遇的位置开始遍历,两个指针每次走一步,则它们会在入环的第一个结点相遇

如图所示:

 证明:为什么这样做可以得到结果?

        ①.设链表不带环部分的长度为a,环的周长为c,相遇点的后半部分为b,则相遇点前半部分为c-b

         ②.首先,无论这个链表的环形部分多长多短,不是环形部分多长多短,在快慢指针相遇时,慢指针的路程绝对不会超过一圈。如下图所示:(在这个链表的情况下,快慢指针不会出现这样的情况,即不会是在这些位置,只是用来演示极限情况

  

        ③.在慢指针入环之前,快指针可能已经走了n圈

        ④.所以得到路程表达式:

                慢指针:S1=a+(c-b)

                快指针:S2=a+nc+(c-b)

        ⑤.快指针路程是慢指针的两倍

        ⑥.所以得到:2*S1=S2

        ⑦.带入表达式得:  2*a+2*(c-b)=a+nc+(c-b)

                                        a+c-b=nc

                                        a=n(c-1)+b 

这个公式表示:一个指针cur从头出发,slow指针从相遇点出发, 两个指针每次都走一步,当cur走a步走到入环口的时候,slow指针从相遇点处走了n圈加上b,即入环处。

leetcode解题代码 

struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode* fast=head;//快慢指针都从头出发
    struct ListNode* slow=head;
    while(fast!=NULL && fast->next!=NULL)
    {
        slow=slow->next;//慢指针走一步
        fast=fast->next->next;//快指针走两步
        if(fast==slow)
        {
            break;
        }
    }
    if(fast==NULL || fast->next==NULL)//如果快指针走到NULL,说明不带环,返回空
    {
        return NULL;
    }
    fast=head;//快指针从头开始走,每次一步
    while(fast!=slow)
    {
        fast=fast->next;
        slow=slow->next;
    }
    return fast;
}

  • 31
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值