c语言数据结构单链表中的环形链表问题

目录

1.典型的单链表中环形链表的题

2.解题思路

3.代码实现:

4.延伸问题

  4.1 为什么slow和fast一定会在环中相遇?会不会在环里面错过,永远遇不上?请证明一下:

  4.2 为什么你要让slow每次走一步,fast每次走两步呢? 能不能fast一次走n步(n>2)?请证明一下?

  4.3 slow走1步,fast走2步,一定会相遇。如何求环的入口点呢?


1.典型的单链表中环形链表的题

给你一个链表的头节点 head ,判断链表中是否有环。

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

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

2.解题思路

1. 定义两个指针fast和slow。

2. fast快指针一次走两步,slow慢指针一次走一步。

3. 不带环,fast就会为空。  

4. 带环,fast就会在环里面追上slow。

3.代码实现:

struct ListNode {
    int val;
    struct ListNode *next;
};
 
bool hasCycle(struct ListNode *head) {
    struct ListNode *slow = head;
    struct ListNode * fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        {
            return true;
        }
    }
    return false;
}

4.延伸问题

  4.1 为什么slow和fast一定会在环中相遇?会不会在环里面错过,永远遇不上?请证明一下:

证明:

 第一步: slow和fast,fast一定是先进环的,这时slow走了入环前距离的一半。

第二步: 随着slow进环,fast已经在环里面走了一段,走了多少跟环的大小有关。

假设slow进环的时候,slow根fast的距离是N。

第三步: fast开始追slow ,fast每次向前走两步,slow每次向前走一步,每追一次,判断一下是否相遇?  每追一次, fast和slow的距离变化为:N,N-1,N-2,……,0  。  

可以发现每追一次,距离减少1,他们之间的距离最后减到0,就是相遇点,所以一定会相遇。

  4.2 为什么你要让slow每次走一步,fast每次走两步呢? 能不能fast一次走n步(n>2)?请证明一下?

证明:

假设: slow一次走1步,fast一次走3步的情况。slow进环以后,fast跟slow 之间的距离为N,fast开始追slow。他们之间的距离变化如下:

N是偶数N是奇数
NN

N-2

N-2
N-4N-4
......
21
0-1

如果N是奇数,距离变成-1意味着他们之间的距离变成了C-1(C是环的长度)。

此时,如果C-1奇数,那么就永远追不上了。如果C-1偶数。那么就可以追上

综上: 以此类推可以看出当n>2时,有可能出现追不上的问题。所以选择让slow每次走一步,fast每次走两步。

  4.3 slow走1步,fast走2步,一定会相遇。如何求环的入口点呢?

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

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

 思路图:

证明:

追上相遇的过程中:

慢指针走的距离:L+X  (因为X是小于C的,慢指针走一圈,快指针能走两圈,期间一定可以相遇了。所以是X,而不是N*X)

快指针走到距离:L+N*C+X (N>=1)   N是他们相遇之前,fast在环里走的圈数。

已知快指针走的路程是满指针的2倍:

2(L+X) = L+N*C+X

L+X = N*C

L = N*C - X

L = (N-1)*C + C-X    

综上:可以看出一个指针从meetNode回到入口处的距离等于另一个指针从头走到入口点处的距离。用一个指针从meetNode走,同时一个指针从头开始走,它俩相遇点就是入口点。

代码实现:

​
struct ListNode {
    int val;
    struct ListNode *next;
};
 
bool hasCycle(struct ListNode *head) {
    struct ListNode *slow = head;
    struct ListNode * fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        {
            // 相遇
            struct ListNode *meetNode = slow;
            // 公式证明的
            while(meetNode != head)
            {
                meetNode = meetNode->next;
                head = head->next;
            }
            return meetNode;
        }
    }
    return NULL;
}

​

 

最后,这是我在学习单链表中的环形链表时的笔记,如果有错误的地方希望大家指正。大家一起努力!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值