当前位置: 首页 > news >正文

一个冷门算法——Floyd判圈算法在Leetcode中的应用

题目

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。示例 1:输入:nums = [1,3,4,2,2]
输出:2
示例 2:输入:nums = [3,1,3,4,2]
输出:3
示例 3 :输入:nums = [3,3,3,3,3]
输出:3

先搬上我的解法——人类的记忆机制。

人类的记忆机制

最自然的想法就是一个一个地去看,从左往右,第一个开始,然后看看以前出现过的元素我见过没。圈起来的应该不算额外的空间复杂度开支吧?(我只是把我见过的元素给圈起来了而已,反而是Python的索引其实是有机制会导致额外的时空复杂度开支)

class Solution:def findDuplicate(self, nums: List[int]) -> int:mem = set()for i,j in enumerate(nums):if j in mem:return jmem.add(j)

我看到一个题解很有意思,顺便学了一下Algorithms书里面不会提到的一些算法。首先我用非常数学的办法分析了一下这个题目的特征。

等价类的划分

这题有一些特征:

  1. nums长n+1,这意味着其索引的范围是[0,n]
  2. nums中元素的范围则是[1,n],被[0,n]完全包含

这就意味着可以把nums中的元素拿出来抽“索引”。现在定义一个函数fff
f(x)=nums[x],x∈numsf(x) = \text{nums}[x], x\in \text{nums}f(x)=nums[x],xnums
这就定义了一个nums上的自我变换。
明显这个从nums到nums的变换不是一个双射,至少第一个元素没有任何原像可以映射到它,因此f(x)f(x)f(x)的值域一定是其定义域的真子集。nums中只有一个数字是重复的,很明显就是因为这个重复的数字导致了fff不是双射。我们回顾一下没有重复数字的情况。这种情况下,可以定义nums上的全排列,根据抽象代数的基本知识可知,全排列中的每一个数字都属于某个循环群,这些循环群是全排列对应集合的一个分割。现在我们缩小目光,只考虑fff在其值域上的限制。fff在其值域上的限制如果是个满射,一定是个单射,因此是双射,形成值域内的全排列。所以,fff在其值域上的限制内一定会有循环群(但是未必分割这个值域),如果路径到了循环群,路径可以建模成一个只有一个环的链表。

我们知道从nums[0]出发,肯定是在上述的循环群外面出发。

然后再进入这个循环群。那么这时场景就可以建模成为带有一个环的链表。

龟兔赛跑算法及其在本题上的证明

这是一个在链表上的算法,又称Floyd判圈算法。通过等价类的划分,我们已经知道关于圈一些基本事实,以及为什么可以建模成为带一个环的链表(设这个环的长度为n),那么接下来,就介绍一下这个龟兔赛跑算法。

该算法用了一快一慢两个指针,快指针的速度是2,慢指针的速度则是1。第一步,将两个指针都置于链表的起点。然后,Run起来!

我们标注一下慢指针踏入环的瞬间,不妨标记此时慢指针已经走了l步(但是我们并不知道这个l是多少)。现在按照顺序给环标号,比如慢指针进来的地方是0。显然,这个时刻快指针一定是在环内的。设这个时刻,此时快指针比慢指针在环中快m步(m<n),那么这个时候快指针在环中的位置就是m。我们稍稍看看这个m是多少。如果不考虑环的效应,快指针原来应该领先慢指针l步,现在考虑环的效应,那么有l%n=m。
因为快指针的速度是2,慢指针的速度是1,二者同向,那么速度差为1。慢指针每走一步,快指针与慢指针的距离就会增加1。我们是在环中计算的,这个增加有上限,当增加到n的时候,就是两个指针碰面的时刻。所以,不论原来的m是多少,两个指针一定会碰面。所以,两个指针在环中标号为n-m的地方碰面(快指针在m+2(n-m)%n=n-m的位置)。

下面这个算法要找入口。现在这俩指针的位置一样,把快指针的速度降为1,然后放到链表的起点。原来的慢指针不动。现在继续让它们跑起来。

显然,接下来它们都需要走l步。Floyd判圈算法神奇的地方在于,这时候这两个指针会相遇。让我们验证一下这个结论。圈里的指针位于n-m+l处,而圈外指针走了l步后到达0处。神奇的是(n-m+l)%n=(n+l-m)%n=(n+kn+m-m)%n=0。这就意味着两个指针是相遇的!指针的位置,就是环入口(在环里)的位置。这个值,恰好就是重复的那个数字。

class Solution:def findDuplicate(self, nums: List[int]) -> int:slow = nums[0]fast = nums[0]fast = nums[nums[fast]]slow = nums[slow]while fast!=slow:fast = nums[nums[fast]]slow = nums[slow]fast = nums[0]while fast!=slow:fast = nums[fast]slow = nums[slow]return fast

文章转载自:

http://TfoB0wBX.mhsmj.cn
http://vNKu3AyY.mhsmj.cn
http://3AwIpwoY.mhsmj.cn
http://1b809lC8.mhsmj.cn
http://1rdCWsm3.mhsmj.cn
http://RGZwbOCW.mhsmj.cn
http://pbDGDfYK.mhsmj.cn
http://M30AXgdj.mhsmj.cn
http://nNibCWlJ.mhsmj.cn
http://02qdcBOk.mhsmj.cn
http://gzKpj9UV.mhsmj.cn
http://g9znKJ8f.mhsmj.cn
http://3v87vGfJ.mhsmj.cn
http://kuxso54n.mhsmj.cn
http://6xtRHyme.mhsmj.cn
http://3TZbgZaw.mhsmj.cn
http://eoETtdtq.mhsmj.cn
http://lAKEo7rZ.mhsmj.cn
http://OC4E2xHg.mhsmj.cn
http://aKbtuhw4.mhsmj.cn
http://g7bCpAtq.mhsmj.cn
http://cxOuTRWy.mhsmj.cn
http://p1u3mzkP.mhsmj.cn
http://QW2YzWOY.mhsmj.cn
http://xwNtf74a.mhsmj.cn
http://7jlCJf2u.mhsmj.cn
http://KDSXkXE3.mhsmj.cn
http://atBVo4Bu.mhsmj.cn
http://PXJbLE78.mhsmj.cn
http://tiVixPf5.mhsmj.cn
http://www.dtcms.com/a/383680.html

相关文章:

  • 程晓明在链改 2.0 六方会谈首提 “双众筹 + 私交所” 回应 RWA 国内落地可行方案
  • 【车载audio开发】【基础概念1】【音频基础概念通俗讲解】
  • O3DE社区发布2505.0版本
  • VSCode 的 Run and Debug 下拉菜单都是怎么配置的,原理是什么?
  • Ligero 和 Brakedown PCS中的tensor product结构
  • 高效群签名方案与优化方法
  • [第一章] web入门—N1book靶场详细思路讲解(二)
  • 行优先 vs 列优先:性能差异揭秘
  • python把文件从一个文件复制到另一个文件夹
  • 平衡车 -- 遥控器
  • 深度学习(八):学习率
  • VSCode使用prettier插件进行格式化配置
  • 前后端分离项目如何解决跨域问题
  • IDEA使用Maven和MyBatis简化数据库连接(实现篇)
  • 【Pywinauto库】12.2 pywinauto.element_info 后端内部实施模块
  • 正向代理与反向代理的异同
  • 从ENIAC到Linux:计算机技术与商业模式的协同演进——开源生态的崛起与重构
  • RTC驱动原理
  • MyBatis 的“魔法”:Mapper 接口是如何找到并执行 SQL 的?
  • 构建日志采集和分析平台
  • 《Unity+腾讯云TRTC故障排查指南:从日志盲区到线程死锁的全链路解析》
  • 笔记25.9.14(QueryWrapper,Builder ,Stream流处理,forEach)
  • 深入理解MySQL主从架构中的Seconds_Behind_Master指标
  • systemverilog如何解决不能使用变量索引来进行位选择的范围指定
  • 多语言编码Agent解决方案(1)-项目概述与架构
  • 【深度学习踩坑实录】从 Checkpoint 报错到 TrainingArguments 精通:QNLI 任务微调全流程复盘
  • 【愚公系列】《人工智能70年》019-语音识别的历史性突破(铲平技术高门槛)
  • webpack 配置文件中 mode 有哪些模式?
  • AI推理范式:从CoT到ReAct再到ToT的进化之路
  • webpack和Module Federation区别分析