13.环形链表Ⅰ(双指针)14.环形链表Ⅱ(双指针)(有公式推导哦)

环形链表 试试这题

给定一个链表,判断链表中是否有环。

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

如果链表中存在环,则返回 true 。 否则,返回 false 。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 3:
在这里插入图片描述

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

解题思路:带环的链表让人头疼的地方就是不能遍历链表,因为如果你遍历链表就会链表死循环,这是我们就只有有双指针来解决这个问题,定义两个指针fast和slow指针,fast指针每次走两步,slow指针每次走一步,当两个指针同时从head头指针开始遍历,因为fast指针移动比较快先进入环里面(如果有的话)
,然后因为fast指针每次都比slow指针快一步,所以在里面形成了追击问题,每次再环里面两个指针的差距都会减少一,直到最后相遇,如果中间fast指针或fast->next为NULL,则直接判为不带环

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
typedef struct ListNode Node;
bool hasCycle(struct ListNode *head) {
    //如果只有一个结点或者头结点为空则一定不是带环链表
    if(head == NULL || head->next == NULL)
        return false;
    Node* fast = head;
    Node* slow = head;
    //判断fast指针或fast->next指针是否为空
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(slow == fast)//如果slow和fast指针相遇,那么就是带环指针
            return true;
    }
    return false;//如果跳出循环,说明链表不带环
}

那为什么sloe指针走一步fast指针走两步呢?如果fast指针走3,4,5…N步行不行呢?

下证:
因为fast指针和slow指针都会进环的(如果有环的话)那就假设指针都已经进环了,
在这里插入图片描述
这时fast指针其实就是在在这个环里在追赶slow指针,只要fast追赶slow超了一圈,那就追上了,,假设fast和slow相差N步,(fast每次2步,slow每次1步)每走一次他们的距离就减少1,距离变化:N-1,N-2,N-3…1,0。最后变成0,但是要是fast每次走3步呢?每次距离较少2,N-2,N-4…如果环中的节点数为奇数个最后指针一步的时候就变成-1距离了,那就又相差一步了,这样下去又变成死循环了,如果是4步也是同样的道理,每次快3步,结点数为偶数,那也会死循环,所以总结就是相差距离N必须是每次减少距离的倍数(相加距离N % (fast每次步数 - slow每次步数) == 0)才不会死循环,又因为1是任何数的因数所以每次fast指针不slow指针快1步。

环形链表 II(在带环链表的入口点)试试这题

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

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。

说明:不允许修改给定的链表。

示例 1:

在这里插入图片描述

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 3:

在这里插入图片描述

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 
typedef struct ListNode Node;
struct ListNode *detectCycle(struct ListNode *head) {
    //先找到相遇点
    Node* fast = head;
    Node* slow = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
            break;
    }
    //判断链表是否带环,如果fast或fast->next为NULL,说明不带环
    if(fast == NULL || fast->next == NULL)
        return NULL;
    //如果带环则,让fast指针或slow指针和head指针一起走,他们相遇的点就是换的入口点
    slow = head;
    while(fast != slow)
    {
        fast = fast->next;
        slow = slow->next;
    }
    return fast;
}

在这里插入图片描述
head到入环口距离k,入环口到相遇位置距离x,环的周长为C.
slow指针走距离,fast指针走k+x+NC距离,N是一个系数,环越大fast在和slow指针先相遇前绕环的圈数越少,环越小绕环次数越大(N越大),fast = 2*slow,
2(x+k)= k+x+NC
x+k = NC
k = NC-x
k = (N-1)C + C - x
所以头指针到入环位置等于相遇位置到入环位置,因为中间fast指针已经饶了N-1圈环长所以所以还是是相遇位置,最特殊的时候就是当N=1时,两个位置相等,这样通过fast指针和head指针同时走相遇时就可以找到入口点

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hyzhang_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值