C语言算法刷题笔记

环形链表

一、题目描述

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

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 

二、解题思路

 使用双指针

  1. 双指针第一次相遇: 设两个指针 fastslow 都指向链表的头部 headfast 指针每次走 2 步,slow 指针每次走 1 步;
    1. 第一种情况: fast 指针走过链表末端,说明链表无环,直接返回 null;

      TIPS:如果链表有环,两指针一定会相遇。因为每走 1轮,fast 与 slow 的间距 +1,fast 指针一定会追上 slow 指针;

    2. 第二种情况: fast == slow时, 两指针在环中 第一次相遇 。fast指针和slow指针走过的步数如下:

      1. 假设链表共有 a + b 个节点,其中链表头部到链表入口 有 a 个节点(不计链表入口节点), 链表环有 b 个节点。设fast指针走了 f 步,slow指针走了 s 步,则有:

        1. fast 指针走的步数是slow指针步数的2倍,即 f = 2s;(解析:fast 每次走 2 步)
        2. fast 比 slow 多走了 n 个环的长度,即 f = s + nb;(解析:两个指针都走过 a 步,然后在环内绕n圈直到重合,相遇时 fast 指针比 slow 指针多走环的长度n倍 );

        3. 以上两式相减得:f = 2nb,s = nb,即 fast 和 slow 指针分别走了 2n 个,n个环的长度 (注意: n是未知数,不同链表的情况不同)。

  2. 目前情况分析
    1. 如果让指针从链表头部一直向前走并统计步数 k ,那么所有走到链表入口节点时的步数是:k = a+nb(先走 a 步到入口节点,之后每绕 1 圈环( b步) 都会再次到入口节点)。
    2. 第一次相遇时,slow 指针走过的步数为 nb 步。因此,我们只要想办法让 slow 再走 a步停下来,就可以到环的入口。
    3. 但是我们不知道 a 的值,该怎么办?依然是使用双指针法。我们构建一个指针,此指针需要有以下性质:此指针和slow 一起向前走 a 步后,两者在入口节点重合。那么从哪里走到入口节点需要 a步?答案是链表头部head节点。
  3. 双指针第二次相遇:
    1. slow 指针 位置不变 ,将 fast 指针重新 指向链表头部节点 ;slow 和 fast 同时每轮向前走 1 步;TIPS:此时 f = 0,s = nb ;

    2. 当 fast 指针走到 f = a 步时,slow 指针走到步 s = a+nb,此时两指针重合,并同时指向链表环入口 ,此时slow指向环的入口节点。
  4. 返回slow指针指向的节点

复杂度分析

  • 时间复杂度:O(N),其中 N为链表中节点的数目。在最初判断快慢指针是否相遇时,slow 指针走过的距离不会超过链表的总长度;随后寻找入环点时,走过的距离也不会超过链表的总长度。因此,总的执行时间为 O(N)+O(N)==O(N)。
  • 空间复杂度:O(1)我们只使用了slow,fast 两个指针。

三、代码实现

// 环形链表长b,链表头到环形链表入口长度为a,总长a+b
// 第一次相遇 快指针比慢指针多走n圈
// 快指针步数f=2s=s+nb=2nb 慢指针步数f=s=nb
// 慢指针指向环形链表入口需要走a+nb步
// 将快指针重新指向链表头,同时走a步,快、指针同时指向环形链表入口
struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode *fast = head;
    struct ListNode *slow = head;
    while(fast != NULL){
        slow = slow->next;
        if(fast->next == NULL)
            return NULL;
        fast = fast->next->next;
        if(fast == slow){
            fast = head;
            while(fast != slow){
                fast = fast->next;
                slow = slow->next;
            }
            return fast;
        }
    }
    return NULL;
}

                        

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值