【面试题】链表成环?求入环点?证明+代码?必须安排~

在这里插入图片描述

👦个人主页:@Weraphael
✍🏻作者简介:目前学习C++和算法
✈️专栏:Leetcode + 面试/笔试
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注✨


一、环形链表I

1.1 题目描述

LeetCode链接:环形链表I
在这里插入图片描述

1.2 思路 + 代码实现

【思路】
可以使用快慢指针,然后转化成追击问题。快指针一次走2步,慢指针一次走1步,如果链表成环,快指针就一定能追上慢指针。
此篇博客详细讲述了快慢指针 —> 点我跳转

【代码实现】

bool hasCycle(struct ListNode *head) 
{
    struct ListNode* fast = head,*slow = head;
    while (fast && fast->next)
    {
        //快指针一次走2步,慢指针一次走1步
        fast = fast->next->next;
        slow = slow->next;
        
        if (fast == slow)
            return true;
    }
    return false;
}

在这里插入图片描述

1.3 证明

写出以上代码并不难,关键在于证明。

  • 为什么慢指针slow走1步,快指针fast走2步,它们一定就能相遇?有没有可能会错过?请证明。

【证明】
假设慢指针slow进环时,设快指针fast和慢指针slow之间的距离是Nslow进环后,fast就开始追加slowslow每走一步,fast就会走2步,它们之间的距离就会逐渐缩小1。
fastslow之间的距离变化:
N — 起始距离
N-1
N-2

3
2
1
0 – 相遇
直到N的距离逐渐缩小到0,它们就一定能相遇。

  • 假设慢指针slow走1步,快指针fast走x步(x ≥ 3),它们会相遇还是会错过?请证明。

【证明】
假设x = 3,与上面同样的道理,设slow进环时,fastslow之间的距离为Nslow进环后,fast就开始追击slowslow走1步,fast走3步,它们之间的距离就会逐渐缩小2。但这次的证明与上一个不同,因为上一个之间的距离逐渐缩小1,所以无论N的值是多少,N最后一定会被减到0。而这次证明需要对N进行分类讨论,N是偶数或奇数。
fastslow之间的距离变化:
①当N是偶数时,再加上fastslow的距离逐渐缩小2,最后的它们之间的距离一定为0
N — 起始距离
N - 2
N - 4

4
2
0 – 相遇
②当N是奇数时
N
N - 2
N - 4

3
1
-1
当走到最后,fastslow之间的距离为-1,这说明fast在追加slow的时候超过slow了,此时fastslow之间的距离就是周长 - 1(往后假设周长为C),相当于进入新一轮追击。这里就要再判断C - 1是奇数还是偶数,如果C - 1是偶数代表一定能追的上,否则就追不上。

二、环形链表II

2.1 题目描述

LeetCode链接:环形链表II

在这里插入图片描述

2.2 思路 + 代码

【思路】
先给出结论:一个指针从相遇点开始走,另一个指针从起始点开始走。它们最终会在入环点相遇。 后面会给出i详细的证明过程

【代码实现】

struct ListNode *detectCycle(struct ListNode *head) 
{
    //快慢指针找相遇点
    struct ListNode* fast = head,*slow = head;
    while (fast && fast->next )
    {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast)
        {
            //相遇点(slow改成fast也行)
            struct ListNode* meet = slow;
            //记录起始点
            struct ListNode* start = head;
            while (start != meet)
            {
                meet = meet->next;
                start = start->next;
            }
            //最终他们会在入环点相遇
            return meet;
        }
    }    
    return NULL;
}

在这里插入图片描述

2.3 证明

面试官:你怎么知道一个指针从相遇点开始走,另一个指针从起始点开始走,它们最终会在入环点相遇。请证明

在这里插入图片描述
【证明过程】

  1. 设起始点->入环点的距离为L
    入环点->相遇点的距离为X
    环的长度为C
  2. 所以,慢指针移动的距离:L + X
    快指针移动的距离:L + nC + X
  • 为什么快慢指针相遇时,慢指针的移动距离小于环长,换句话说,为什么慢指针的移动距离不是L + X + n
    答:考虑最坏的情况下,当慢指针入环时,快指针敲好在慢指针前面,快指针则需要走C - 1步才能与慢指针相遇,所以无论对于任何情况,快指针到慢指针的距离都不会超过C - 1步,所以,慢指针的移动距离更不会大于环长
    在这里插入图片描述

  • 快指针的移动距离不能写成L + C + X
    答:因为起始点到入环点和相遇点到入环点有可能不相等。
    在这里插入图片描述

  1. 规定快指针走2步,慢指针走1步。所以,快指针的移动距离是慢指针的2倍。
    所以,不难可以列出等式:2(L + X) = L + nc + X
  2. 化简等式得:L = nC - X --> 意味着:一个指针从相遇点走,另一个指针从起始点走,它们最后会在入口点相遇。

5、总结

若证明过程有啥问题的,欢迎大佬指出,虚心接受orz

  • 19
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
### 回答1: 可以使用双指针法来判断链表是否有环。 具体来说,可以定义两个指针:一个指针每次移动一步,另一个指针每次移动两步。如果链表有环,那么两个指针最终一定会相遇;如果没有环,那么两个指针就会到达链表的末尾,退出循环。 下面是一个示例代码: ``` public boolean hasCycle(ListNode head) { if (head == null || head.next == null) { return false; } ListNode slow = head; ListNode fast = head.next; while (slow != fast) { if (fast == null || fast.next == null) { return false; } slow = slow.next; fast = fast.next.next; } return true; } ``` 其中,`ListNode` 是链表的节点类型,`slow` 和 `fast` 分别表示慢指针和快指针。 注意:在判断快指针是否为空时,要同时判断它的下一个节点是否为空,因为快指针每次会跳两步,如果只判断快指针是否为空,在快指针的下一个节点为空的情况下,快指针就会跳到一个空指针上,导致程序异常。 ### 回答2: 要判断一个链表是否有环,可以使用快慢指针的方法。 首先,定义两个指针fast和slow,初始化都指向链表的头节点。 然后,循环遍历链表,每次fast指针走两步,slow指针走一步。 如果链表中有环,则fast指针最终会追上slow指针,即fast == slow。 如果链表中没有环,则fast指针会先到达链表的末尾,即fast == null。 因此,当fast == slow时,我们可以得出结论:链表有环;当fast == null时,我们可以得出结论:链表无环。 以下是用Java代码实现此方法: ```java public boolean hasCycle(ListNode head) { ListNode fast = head; ListNode slow = head; while (fast != null && fast.next != null) { fast = fast.next.next; slow = slow.next; if (fast == slow) { return true; } } return false; } ``` 这样,我们就可以通过快慢指针的方法判断一个链表是否有环。 ### 回答3: Java中判断链表是否有环可以使用快慢指针的方法。 快慢指针是指我们在遍历链表时使用两个指针,一个指针每次移动两个节点,另一个指针每次移动一个节点。如果链表中有环,则快指针最终会追上慢指针,如果没有环,则快指针会先到达链表尾部。 具体实现如下: ```java public boolean hasCycle(ListNode head) { if (head == null || head.next == null) { return false; } ListNode slow = head; ListNode fast = head.next; while (slow != fast) { if (fast == null || fast.next == null) { return false; } slow = slow.next; fast = fast.next.next; } return true; } ``` 这段代码首先判断了链表是否为空,或者链表只有一个节点,这种情况下肯定没有环。然后初始化快慢指针,慢指针每次向前移动一个节点,快指针每次向前移动两个节点。如果链表有环,那么快指针最终会追上慢指针,返回true;如果快指针到达了链表尾部,说明没有环,返回false。 该方法的时间复杂度是O(n),其中n是链表的长度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值