题目描述
思路解析动画文字版
好懂,但 seen 要占 O(n) 额外空间,过不了进阶要求。面试官追问「能不能不开 seen」,就得换思路。
负号不改变数字大小,只是借符号位当一个「出现过」的标记。读数字时永远用 abs,就不怕它已经被打过勾。
映射规则:数字 x 站到下标 x−1:记住这条映射:合法数字 1 到 8,正好对应下标 0 到 7。数字 1 找下标 0,数字 8 找下标 7,一一对应不浪费。
第一遍 · x=4:给下标 3 打勾:读到 4,说明数字 4 出现过。跳到下标 3,nums[3]=7 是正的,取负变 −7。以后看见下标 3 是负数,就知道 4 已被标记。
第一遍 · x=3:给下标 2 打勾:读到 3,跳到下标 2,把 2 取负成 −2。注意标记的是「下标 2」这个格子,和它原来装的值 2 没关系。
第一遍 · x=2:给下标 1 打勾:读到 2,给下标 1 打勾。走到这里 2、3、4 都见过了。
第一遍 · 读到 −7:abs 还原成 7:这就是 abs 的用武之地。下标 3 此刻是 −7,但它原来代表的数字是 7。用 abs 拿回 7,跳到下标 6 打勾。如果偷懒不取 abs,下标就会算错。
第一遍 · x=8:给下标 7 打勾:读到 8,给最后一格下标 7 打勾,1 变 −1。
第一遍 · 重复的 2:下标 1 已是负数,跳过:第二个 2 又指向下标 1,但 nums[1] 已经是 −3。这里必须判一句「只有大于 0 才取负」,否则会把它翻回正数,等会儿误判成缺失。这是本题第一大坑。
第一遍 · 读到 −3:abs=3,下标 2 已负,跳过:下标 6 现在是 −3,abs 还原成 3,本想给下标 2 打勾,但下标 2 早被标过了,跳过。abs + 判正负这套组合,让重复数字怎么读都不出错。
第一遍 · x=1:给下标 0 打勾,扫描结束:最后读到 1,给下标 0 打勾,4 变 −4。第一遍走完,所有出现过的数字都在数组里留下了「负号脚印」。
第二遍 · 还为正的下标就是答案:第二遍扫描,只有下标 4 和 5 还是正数,说明 5 和 6 没被打过勾。下标 4 转成 5,下标 5 转成 6,答案就是 [5,6]。注意:要返回的是「下标+1」,不是格子里那个值。
灵魂帧慢放:一次完整打勾的前→中→后:把最容易绕晕的一步慢放:当前格 −7 是「被读的人」,下标 6 是「被打勾的人」,两者不是同一个。读靠 abs、打勾靠 abs(x)−1,永远分清这两个角色,这题就通了。
长度 n、值在 1 到 n,这个信号一出现,就该想「值映射成下标 + 原地标记」。负号、置换、计数都是同一招的变体。
雷区实演:[1,1] 不判正负会翻车:拿 [1,1] 真跑一遍:第一个 1 把下标 0 标成 −1,第二个 1 如果不判「大于 0 才取负」,就会把 −1 又翻回 +1。第二遍一看下标 0 是正的,错把 1 当成缺失,输出错的 [1]。正确答案是 [2]。
面试追问:把动画里的「值映射下标 + 符号当标记」讲成自己的话,再补一句「不让改数组就用排序或 seen」,追问就稳了。
想系统练这一类「数组当哈希表」的题,去专题页跟着练,遇到卡点直接问 AI 助教小欧,或开通关训练把同类题一次刷透。
边界三连:三个极端:填满、无缺失、有重复。重点盯 [2,2] —— 第二个 2 来时下标 1 已是负数,必须跳过,最后下标 0 仍为正,答案 [1]。
参考代码
class Solution: def findDisappearedNumbers(self, nums): for x in nums: # 第一遍:打勾 i = abs(x) - 1 # 读值要取 abs if nums[i] > 0: # 只给没打过的取负 nums[i] = -nums[i] ans = [] for i, x in enumerate(nums): # 第二遍:收答案 if x > 0: ans.append(i + 1) # 返回下标+1 return ans复杂度
- 时间复杂度:O(n),第一遍打勾、第二遍收答案,各扫一次,2n 步,去常数即 O(n)。
- 空间复杂度:O(1),标记直接写回 nums,只用 i、x 两个变量;返回数组 ans 不计入额外空间。
可套用的代码模板
这是可背骨架:①值映射下标取 abs、②判正才标、③收集条件随题换。骨架不变,448/442 只差最后一句怎么收答案。
# 适用:len(nums)=n 且 值都在 1..n,要找 缺失/重复for x in nums: i = abs(x) - 1 # ① 值→下标,读值务必取 abs if nums[i] > 0: # ② 没标过才标(重复就跳过) nums[i] = -nums[i] # 符号位 = 出现过# ③ 收集条件按题意改:# 448 缺失 → 第二遍取 nums[i] > 0 的 i+1# 442 重复 → 第一遍发现 nums[i] < 0 时记下 i+1易错点
面试追问把动画讲成自己的话
追问为什么能原地标记,不用额外空间?
追问如果不让改原数组呢?
追问和 442「找重复」是什么关系?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
搜索二维矩阵 II
LeetCode 240 · 中等 · 沿着 数组 & 哈希 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题