环形链表及环起始点

关于环形链表

环形链表如下图

在这里插入图片描述

它是一个单链表,但是尾结点->next不指向空及其它本身,而是指向链表中的其他结点,形成一个环。

OK废话不多说,进入正题!

怎么判断链表是否为循环链表!

分享一下我的学习经历

个人经历分享

我第一时间想到的是

创建一个足够大的结点指针数组,

将结点指针一个一个指向结点,

并且每加入一个结点之前判断一轮,该结点是否被数组中的结点指针指向过,

若有,则为环形链表。

代码如下:

struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode *a[100000];//可以称之为in数组,判断结点是否in这个数组中
    int n=0;//每存储一个元素n++,记录a[]中的元素个数
    struct ListNode* slow = head;//该指针用于一步一步遍历链表
    while(slow)
    {
        a[n]=slow;
        slow=slow->next;
        n++;
        for(int i =0;i<n;i++)
        {
            int count=0;
            if(a[i]==slow)//若该结点出现在数组中
	//则为环形链表,且此时slow为环形链表中环的起始点
            {			
                return slow;
            }
        }
        
    }
    return NULL;//若循环正常退出,则不存在环
}

这是一个时间复杂度高,且空间复杂度高的做法,很暴力。

接下来介绍快慢指针

快慢指针

在这里插入图片描述

如图就是快慢指针的运行方式

fast 的步长为 2

slow 的步长为 1

循环跳出条件为 slow 追上 fast

以及 fast 为 NULL 或 fast->next 为 NULL

若是 slow 追上,则存在环

若是 fast 为 NULL 或 fast->next 为 NULL 跳出,则不存在环

求环起始点

例子:
在这里插入图片描述
在这里插入图片描述

若存在环则返回环的起始结点

若不存在环则返回 NULL

暴力求法

代码如下:

就是利用指针数组存每一个结点,

若遍历指针再次遇到该数组内的结点,

则跳出循环,输出遍历指针所指向的结点

若无,则正常跳出循环,输出 NULL

struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode *a[100000];
    int n=0;
    struct ListNode*slow=  head;
    while(slow)
    {
        a[n]=slow;
        slow=slow->next;
        n++;
        for(int i =0;i<n;i++)
        {
            int count=0;
            if(a[i]==slow)
            {
                return slow;
            }
        }
        
    }
    return NULL;
}

快慢指针

这里利用了一个数学关系

head到环起始点,和快慢指针相遇点到环起始点的关系)

以该图为例
hrwr 的关系
在这里插入图片描述

接下来是比较繁琐的讲解,请家人们耐心看,最好能动手写,体会含义

设:链表起始点为 h,环起始点为 r,链表总长为 Lw 为快慢指针相遇点

hr 的距离为 arw 的距离为 b(绿色部分)

环的长度为 k(红色加绿色的部分)

因为 快指针慢指针 速度的 两倍

可以得到以下数学关系:
L = a + k L = a + k L=a+k

S l o w = a + b Slow = a + b Slow=a+b

F a s t = a + b + n k = 2 S l o w Fast = a + b + nk = 2Slow Fast=a+b+nk=2Slow

OK经过咱们最强大脑的演算得到:
a + b = n k = ( n − 1 ) k + k a + b = nk = (n - 1)k + k a+b=nk=(n1)k+k

L = a + k L = a + k L=a+k
所以
a + b = ( n − 1 ) k + L − a a + b = (n - 1)k + L - a a+b=(n1)k+La

a = ( n − 1 ) k + ( L − a − b ) a = (n - 1)k + (L - a - b) a=(n1)k+(Lab)

这里我们可以发现(n - 1)k实际上可以去掉,细品一下

得到
a = L − a − b (红色部分) a = L - a - b(红色部分) a=Lab(红色部分)
此时就可以来解释为什么相遇后要令

**fast **步长改为1,slow 回到 head

此时 slowr 点,与 fastr 点距离一致(fast 是在 w 点)

故继续走下去必定相遇,且在起始点 r

附上代码:

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)
        {
            slow=head;
            while(slow!=fast)
            {
                slow=slow->next;
                fast=fast->next;
            }
            return slow;
        }
    }
    return NULL;
}



关于求环起始点,我认为用数学关系可以很一目了然的看出来,但是仔细想其中的道理我还是一知半解的样子,于是便用数学关系来解释这个地方。



题目来源:力扣网

参考资料:关于快慢指针寻找环入口证明

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值