快慢指针在链表中的一些证明

目录

一、一定会相遇的证明

二、环长度

三、连接点

四、带环链表总长度

五、例题


一、一定会相遇的证明

1、如果链表没有环,那么快指针比慢指针先到达尾部(null)

2、如果链表有环的话,因为快指针走的比慢指针快,所以在环中相遇的过程可以看作是快指针从环后边追赶慢指针的过程。

用递归法证明,快慢指针一定会相遇:

(1)快指针与慢指针之间差一步。此时继续往后走,慢指针前进一步,快指针前进两步,两者相遇。
(2)快指针与慢指针之间差两步。此时继续往后走,慢指针前进一步,快指针前进两步,两者之间相差一步,转化为第一种情况。
(3)快指针与慢指针之间差N步。此时继续往后走,慢指针前进一步,快指针前进两步,两者之间相差(N+1-2)即N-1步。重复这个过程,直到快指针和慢指针相遇。

因此,此题得证。所以快指针必然与慢指针相遇。

 

推导:慢指针进入环后,快指针最多多绕一个圈。

快指针F先进环,慢指针S后进。

假设慢指针进环那一刻快指针差m步能追上(0<= m < Length环),根据上边结论,两个指针走m次就会相遇了。

因为m < Length环,所以快指针在慢指针进环那一刻最多比慢指针多绕一个圈。

 

二、环长度

快指针和慢指针第一次相遇时的节点pq(碰撞点),快指针和慢指针从该点开始继续往前走,再次碰撞时所用的操作数就是环的长度Length环。

证明:由上边的推导可得,这里的m为Lengh环。

 

三、连接点

 

假设慢指针进入环中时,即连接点p,快指针(q)需要m步才能追上慢指针。

p和q第一次相遇时,碰撞点在pq处。此时,p走到pq时用了m步。

 假设head到p的距离为a,环长度为Length环,慢指针走了s步,则快指针走了2s步。

从上图可知:

s = a + m

2s = a + m + n * Length环(n为快指针绕环的圈数)

可得

a = n * Length环 - m

也就是:若在头结点和相遇结点分别设一指针,同步(单步)前进,则最后一定相遇在环入口结点p。

可根据这个结论来找到入口节点。

四、带环链表总长度

找到连接点p后,求head到p的长度,再加上环的长度,即为链表的总长。

 

五、例题

给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。

示例 1:

输入: [1,3,4,2,2]
输出: 2

示例 2:

输入: [3,1,3,4,2]
输出: 3

说明:

  1. 不能更改原数组(假设数组是只读的)。
  2. 只能使用额外的 O(1) 的空间。
  3. 时间复杂度小于 O(n2) 。
  4. 数组中只有一个重复的数字,但它可能不止重复出现一次

【代码】

class Solution {
public:
    int findDuplicate(vector<int> &nums) {
        int s = nums[0];
        int f = nums[nums[0]];
        while(s != f){
            s = nums[s];
            f = nums[nums[f]];
        }
        f = 0;
        while (f != s)
        {
            f = nums[f];
            s = nums[s];
        }
        return f;
    }
};

 

<think>好的,我现在需要详细理解C语言中使用快慢指针检测链表环的原理。首先,我应该回忆一下链表的基本结构。链表由节点组成,每个节点包含数据和指向下一个节点的指针。环的存在意味着某个节点的指针指向了链表中之前的某个节点,导致链表尾部形成循环。 接下来,快慢指针方法的具体实现。根据用户提供的引用,比如引用[2]提到快指针每次比慢指针快一步,最终会相遇。这说明快慢指针的速度差是关键。通常,快指针每次移动两步,慢指针每次移动一步。为什么这样能检测环呢? 假设链表有环,当慢指针进入环时,快指针已经在环内。由于快指针每次比慢指针多走一步,两者之间的距离每次减少1,最终必然会相遇。这一点在引用[2]和引用[4]中都有提到。比如引用[4]提到如果快指针每次走3步,速度差为2,可能导致无法相遇,这取决于环的长度是否为速度差的因数。但常规情况下使用步长差1更稳妥。 再考虑数学证明。设环外长度为L,环长度为C。慢指针进入环时,快指针的位置可能在环内的某个点。假设相遇时慢指针走了t步,那么快指针走了2t步。快指针比慢指针多走n圈环,即2t = t + nC,解得t = nC。这说明相遇时慢指针走了n圈环的长度。此时,头指针和相遇点指针同时移动,每次一步,最终会在环起点相遇,如引用[1]所述。 需要注意特殊情况,比如链表为空或只有一个节点的情况,此时不会有环。代码实现时需要处理这些边界条件。例如引用[3]中的代码可能涉及这些处理,但具体实现需要看完整代码。 总结下来,快慢指针检测环的原理基于速度差和相遇条件,数学上确保在有环时必然相遇,无环时快指针会先到达链表末尾。</think>### C语言中快慢指针检测链表环的原理 #### 1. 基本思路 快慢指针法(Floyd判圈算法)通过两个指针以不同速度遍历链表。若链表存在环,快指针最终会追上慢指针;若无环,快指针会先到达链表尾部。核心步骤如下: - **慢指针(slow)**:每次移动1步 - **快指针(fast)**:每次移动2步 - **终止条件**:若两指针相遇,则存在环;若快指针遇到`NULL`,则无环[^2][^4]。 #### 2. 数学证明 假设链表环外长度为$L$,环长度为$C$,两指针相遇时: - 慢指针移动距离:$L + a$($a$为环内移动距离) - 快指针移动距离:$L + a + nC$($n$为快指针在环内绕行的圈数) 由速度关系(快指针是慢指针的2倍)可得: $$2(L + a) = L + a + nC \implies L = nC - a$$ 此时,将**头指针**从链表起点出发,**相遇点指针**从相遇点出发,两者每次均移动1步。头指针移动$L$步后到达环入口,而相遇点指针移动$L$步后相当于在环内移动$nC - a + a = nC$,即回到环入口。因此两者必然在环入口相遇[^1][^4]。 #### 3. 代码实现示例 ```c #include <stdbool.h> struct ListNode { int val; struct ListNode *next; }; bool hasCycle(struct ListNode *head) { if (head == NULL || head->next == NULL) { return false; // 空链表或单节点无环 } struct ListNode *slow = head; struct ListNode *fast = head->next; while (slow != fast) { if (fast == NULL || fast->next == NULL) { return false; // 快指针遇到尾部,无环 } slow = slow->next; // 慢指针走1步 fast = fast->next->next; // 快指针走2步 } return true; // 相遇,存在环 } ``` #### 4. 注意事项 - **步长选择**:快指针步长必须为2,若步长大于2(如3步),可能因环长度与速度差不互质导致无法相遇[^4]。 - **边界条件**:需处理链表为空或单节点的情况。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值