链表中环的入口结点 快慢指针法证明

这篇博客详细介绍了如何使用快慢指针法找到链表中环的入口结点,确保空间复杂度为O(1)和时间复杂度为O(n)。算法首先通过两个指针,一个每次移动一步,一个每次移动两步,寻找环的存在。一旦找到环,快指针回溯到头节点,再次同步移动直至相遇,相遇点即为环的入口。此外,文章还对比了使用hash法的不足之处。

链表中环的入口结点 快慢指针法证明

题目

给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。

数据范围: n <= 1000001 <= node.val <= 10000

要求:空间复杂度 O(1),时间复杂度 O(n)。

例如,输入{1,2},{3,4,5}时,对应的环形链表如下图所示:

img

可以看到环的入口结点的结点值为3,所以返回结点值为3的结点。

输入描述:

输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台会根据第二段是否为空将这两段组装成一个无环或者有环单链表

输出描述:

返回链表的环的入口结点即可,我们后台程序会打印这个结点对应的结点值;若没有,则返回对应编程语言的空结点即可。

算法描述

1)定义两个指针均指向头节点,分别为快慢指针;

2)两个指针同时遍历链表,慢指针逐个遍历,快指针隔一个遍历;

3)如任何一个指针指向空节点,则返回空节点;

4)否则回到2),重复2)至4),直到快慢指针指向同一节点;

5)将快指针拨回至头节点;

6)两个指针继续同时遍历链表,均逐个遍历;

7)直到快慢指针指向同一节点,返回指向的节点,此节点即为环的入口。

算法原理

可以将这道题目抽象为一种特殊的追及问题,如图所示,h为起点、e为环的入口、p为两个指针出发后第一次相遇的地方,L1、L2分别为非环区域的长度和环状区域的长度,S1、S2分别为相遇之前慢、快指针运动的长度,m为相遇时e与p的距离,其中长度、距离是节点数量的抽象表达。题目要求环的入口结点,抽象后就是求e点的位置,即L1的长度。

在这里插入图片描述

因为 快指针速度是慢指针速度的两倍
所以 S2 = 2 * S1
依题意与图示易得:
	S1 = L1 + m
    S2 = L1 + L2 + m
所以 S2 - L1 = S1 = L2
p距下一次到e还有:
    Xpe = L2 - m
因为 S1 = L2, L1 + m = L2
所以 Xpe = L2 - m = L1
所以 此时将快指针放回h慢速运动,可以使两指针再e相遇

此算法只占用了快慢指针的额外内存空间,与输入规模无关,空间复杂度为O(1)。以慢指针为参照,慢指针走了L1 + L2 = n个节点,由于两指针是同步运动的,无需考虑另一指针,时间复杂度为O(n),符合题目要求。

代码实现

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def EntryNodeOfLoop(self, pHead):
        fast = pHead
        slow = pHead
        while True:
            if fast.next is None or fast.next.next is None:
                return None
            slow = slow.next
            fast = fast.next.next
            if fast == slow:
                break
        if slow == pHead:
            return pHead
        fast = pHead
        while True:
            fast = fast.next
            slow = slow.next
            if fast == slow:
                return slow

hash法与代码实现

此题中使用hash法有暴力法之嫌,并不高效。遍历节点,将节点hash存储,利用了节点和hash的唯一性,如节点第一次出现第二次,则说明此节点为环的入口。遍历节点造成的时间复杂度如双指针法,为O(n),但每一个节点都需要存储hash会带来额外的内存消耗,空间复杂度为O(n),不符合题目要求。

class Solution:
    def EntryNodeOfLoop(self, pHead):
        nodes = set()
        node = pHead
        while True :
            if node is None:
                return node
            if node.val in nodes:
                return node
            nodes.add(x)
            node = node.next
内容概要:本文介绍了一个关于超声谐波成像中幅度调制聚焦超声所引起全场位移和应变的分析模型,并提供了基于Matlab的代码实现。该模型旨在精确模拟和分析在超声谐波成像过程中,由于幅度调制聚焦超声作用于生物组织时产生的力学效应,包括全场的位移与应变分布,从而为医学成像和治疗提供理论支持和技术超声谐波成像中幅度调制聚焦超声引起的全场位移和应变的分析模型(Matlab代码实现)手段。文中详细阐述了模型构建的物理基础、数学推导过程以及Matlab仿真流程,具有较强的理论深度与工程应用价值。; 适合人群:具备一定声学、生物医学工程或力学背景,熟悉Matlab编程,从事医学成像、超声技术或相关领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①用于超声弹性成像中的力学建模与仿真分析;②支持高强度聚焦超声(HIFU)治疗中的组织响应预测;③作为教学案例帮助理解超声与组织相互作用的物理机制;④为相关科研项目提供可复用的Matlab代码框架。; 阅读建议:建议读者结合超声物理和连续介质力学基础知识进行学习,重点关注模型假设、偏微分方程的数值求解方法及Matlab实现细节,建议动手运行并修改代码以加深理解,同时可拓展应用于其他超声成像或治疗场景的仿真研究。
### 关于PAT Basic Level Practice的测试点及题目解析 #### 题目难度分级 PAT(Programming Ability Test)是由浙江大学举办的计算机程序设计能力考试,分为不同级别。其中乙级即Basic Level主要面向初学者,考察基本编程技能[^1]。 #### 测试点特点 对于PAT Basic Level中的某些特定题目而言,其测试点设置较为严格。例如,在处理字符串匹配类问题时,需要注意算法逻辑中何时应当终止循环以防止不必要的重复计算;而在涉及数值运算的问题里,则可能因为边界条件而增加复杂度[^3]。 #### 编程语言的选择影响 值得注意的是,尽管大部分简单题目可以作为学习某种新语言的良好实践材料,但在实际操作过程中可能会遇到由于所选语言特性而导致难以通过全部测试点的情况。比如Java在面对部分效率敏感型试题时表现不佳,这可能是由于该语言本身的执行速度相对较慢以及内存管理方式等因素造成的。因此有时不得不转而采用其他更适合解决此类问题的语言版本来完成解答[^2]。 ```cpp #include<bits/stdc++.h> using namespace std; int a[100000]; int c=1; void getPrime(){ int flag=0; for(int i=2;i<105000;i++){ flag=1; for(int j=2;j<=sqrt(i);j++){ if(i%j==0){ flag=0; break; } } if(flag==1) a[c++]=i; } } int main(){ int m,n,i,t=1; scanf("%d %d",&m,&n); getPrime(); for(i=m;i<=n;i++){ if(t%10==1){ printf("%d",a[i]); t++; }else{ printf(" %d",a[i]); t++; } if((t-1)%10==0) printf("\n"); } return 0; } ``` 上述C++代码展示了如何实现一个简单的质数打印功能,并且针对输出格式进行了特殊处理以满足特定要求。这段代码很好地体现了编写高效解决方案的重要性,尤其是在应对像PAT这样的在线评测系统时[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值