链表带环问题

文章讲述了如何使用快慢指针方法解决链表中的带环问题,包括基本思路、两种情况分析以及变题后的解决方案。通过计算fast和slow之间的关系来确定链表是否存在环。

今天我们来看一下链表的经典问题,带环问题

这个问题可以算是比较经典的面试题了,接下来咱们看看题目内容

看到这个题目,首先我们应该想到的思路,就是“快慢指针”;

  • 让快慢指针slow 和 fast 都为head,随后将让fast以slow的两倍速度进行移动,具体如下图

  • 然后注意判断,如果是不带环的链表,那么fast或者fast->next中肯定会有一个为空指针,如下图所示

此时代码可以写为:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    struct ListNode* slow=head,*fast = head;

    while(fast&&fast->next)//判断是否带环,以fast为判断条件
    {
       slow = slow->next;
       fast = fast->next->next; 
    }
   return false;
}

之后我们需要解决while循环中的代码,此时我们需要找到规律

  • 当fast和slow走到环内,一共有两种情况,一种是走着相交,一种是永不相交,此时我们需要判断是哪种情况 

1.当fast指针的速度是slow的两倍,那么fast追赶slow,(设fast和slow之间的距离为N),N(偶数)的距离就是N -> N-1 -> N-2 .....  N-(N-1) -> 0,所以在这种情况下,N的值肯定为0。所以肯定相交。

2.当fast的指针速度是slow的三倍,那么fast追赶slow,N的距离N-2 -> N-4 ->N-6 ..... N-(N-1) -> -1

在这种情况下,就要进入第二次追赶(设环的长度为C)如果C为奇数,那么就需要走N+1的距离

又因为N+1是偶数,所以在C为奇数时,fast和slow会相交。

如果C为偶数,那么fast和slow就不可能相交。

  • 其实我们需要一个公式就可以知道,C不可能在是N为偶数的情况下为奇数

 

设head到环入口节点位置的距离为L,fast到slow的距离为N,环的长度为C

3*L = L+x*C+C-N 

2*L = (x+1)C-N

从此得出,在两边都为偶数的情况下,C不可能为奇数

即永远相交

 那么我们补完代码即可解答这个问题。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    struct ListNode* slow=head,*fast = head;

    while(fast&&fast->next)//判断是否带环,以fast为判断条件
    {
       slow = slow->next;
       fast = fast->next->next; 
        if(fast == slow)
        return true;
    }
   return false;
}

  附加问题(变题)

 这次我们重新设,并且用表达式解决

假设fast和slow已经相遇,N,L和C不变(fast为slow速度的两倍)。得出:

2*L=L+x*C+N

L=x*C+N

不管x为何数,以head和slow或fast节点为头开始走都会同时走到环头节点,此时可以写出代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    struct ListNode* slow=head,*fast = head;

    while(fast&&fast->next)//判断是否带环,以fast为判断条件
    {
        slow = slow->next;
        fast = fast->next->next; 
       
        if(slow == fast){
            struct ListNode* meet = slow
            while(meet!=head)
            {
                head = head->next;
                meet = meet->next;
            }
          }return head;
   return NULL;
     }

那么这题就全部解答完成啦。

咱们下次再见哦 

在C语言中,单链表带头结点是指在链表的头部添加一个额外的结点,该结点不存储任何数据,仅用于标记链表的起始位置。引用\[3\]中的代码展示了一个带头结点的单链表的定义,其中node*表示指向一个结点的指针,linklist表示指向链表的指针。 在带头结点的单链表中,头结点的next指针指向链表的第一个实际存储数据的结点。这样做的好处是可以简化链表的操作,例如插入和删除结点时不需要特殊处理链表为空的情况。引用\[1\]和引用\[2\]中的代码展示了在带头结点的单链表中插入和删除结点的操作。 需要注意的是,在使用带头结点的单链表时,需要特别处理头结点的情况,例如在遍历链表时,通常从头结点的下一个结点开始遍历。此外,使用带头结点的单链表可以更方便地处理空链表的情况,因为头结点始终存在。 带头结点的单链表在实际应用中广泛使用,它可以更高效地进行插入、删除和查找等操作,并且可以更好地处理边界情况。 #### 引用[.reference_title] - *1* *2* *3* [带头节点的单链表(C语言版)](https://blog.youkuaiyun.com/zhu134/article/details/130632385)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值