题目描述
思路解析
一句话答案:LeetCode 1040 移动石子直到连续 II 用排序后的两招求解:最大步数取放弃某条端缝后剩下的空位数,最小步数用长 n 的滑动窗口找覆盖石子最多的位置,瓶颈在排序 O(n log n)。
石子在数轴上挪来挪去,最后要报哪两个数
给一排位置互不相同的石子 stones,每步只能拿起当前最左或最右的端点,放进某个空位,且放完后它不能仍是端点;石子连成一段连续位置即停。返回长度 2 的数组 [最少步数, 最多步数]。以题面 stones=[7,4,9] 为例,排序后 4、7、9,最少 1 步(4 挪到 8 得 7、8、9)、最多 2 步,答案 [1,2]。
为什么不能真的一颗颗搬着去试
最直接的想法是模拟搬端点、穷举每步的放法直到连续。可每步有多个空位、还要递归很多层,移动序列随石子数指数级膨胀,稍大就跑不完。让它变简单的前提是先排序——端点、相邻间距、区间能装几颗,全靠位置有序才能直接读出,所以算最大最小前第一步都是把 stones 排成从小到大。
最大步数:放弃一条端缝,把剩下的空格填满
第一步必然动某个端点,它一旦往里搬,外侧那条通向远处的空隙(端缝)就永久作废。于是只有两种选择:放弃最左,剩下 n-1 颗挤进第二颗到最后一颗的区间;或放弃最右,挤进第一颗到倒数第二颗的区间。哪个区间空位多就能多走几步,取大者即最大步数——参考代码正是数每个区间首尾之间能站几个位置、各减掉 n-1 颗石子。
最小步数:拿一个长 n 的窗口滑过去数石子
终局是 n 颗石子占满 n 个连续位置,所以任何宽度不超过 n 的区间都可能是最后落点。让一个长 n 的滑动窗口贴着石子滑,它是一段宽度不超过 n、从左往右挪的区间:窗内就位的石子越多,要从窗外搬进来的越少,本窗步数就是 n 减窗内石子数。右指针每纳入一颗,宽度一超 n 就把左指针右移、压回 n 以内;每个窗口的『n 减窗内数』取最小即最少步数。
那个『差一点就排好』的位置,公式为什么少算一步
有一种排布会坑这条公式:n-1 颗已连成一片,只剩最后一颗落在远处。窗口罩住那 n-1 颗,按『n 减窗内数』像是只差 1 步,可这步走不通——一步收尾只能把远处那颗端点挪到紧邻空位,而那位置又成了新端点,违反『挪完不能还是端点』。正确要 2 步:先把连续段一端挪出一格,再把远处那颗填进中间。n-1 颗连成片时首尾跨度就是 n-2,所以窗内恰好 n-1 颗、且窗口跨度等于 n-2 时,单独把这窗记成 2 步。
排序后的 4、7、9,最大最小分头验一遍
排序得 4、7、9,n=3。最大:放弃最左 4,两颗挤进 7 到 9 共 3 位装 2 颗、1 空位,1 步;放弃最右 9,挤进 4 到 7 共 4 位装 2 颗、2 空位,2 步,取大得 2。最小:长 3 窗口滑到 7、9(宽度 9-7+1=3 没超 n),窗里 2 颗只需搬 4,1 步;跨度 9-7=2 不等于 n-2=1,没踩特例按 3-2=1 记。答案 [1,2]。
不排序、漏减 n-1、忘判特例,答案就偏了
不排序直接套公式最隐蔽:端点、间距、窗口宽度全依赖有序位置,乱序算的量不可信。算最大若只算一侧、或忘末尾减 n-1,会漏掉『放弃较小端缝更划算』的走法。算最小若漏掉那个『n-1 已连续、差一空位、最后一颗在远处』的特例,会把 2 步误报成 1 步。石子本就连续时输出 0。排序 O(n log n) 是瓶颈,最大 O(1)、滑窗 O(n),空间 O(1)。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
记住两句:最大「移一端、丢小端缝、数空格」;最小「滑长 n 窗口、数窗内最多石子、特例留空位算 2 步」。后面每帧都在套它。
先看原始输入,6 颗石子位置乱序排列。所有套路都建立在「排好序」之上,所以第一步永远是排序。
排好序后变成 2、3、5、6、8、11。最左是 2、最右是 11,整段从 2 到 11 一共 10 个位置,里面只占了 6 颗。接下来先算最大、再算最小。
先算最大步数。整段 10 个位置里有 4 个空格。最理想是每一步只填一个空格、慢慢挪。但第一步必须把一个端点往里搬,会放弃它那一侧的端缝,所以要在两种放弃里挑划算的。
第一种:第一步动最左的 2,等于放弃左端缝,往后所有石子都挤进 3 到 11 这段。这段有 4 个空格,每步填一个,最多走 4 步。
第二种:第一步动最右的 11,等于放弃右端缝,石子挤进 2 到 8 这段。这段只有 2 个空格,最多走 2 步。
两种放弃里挑空格多的:4 和 2 取大,最大步数就是 4。直觉上就是「丢掉较小的那条端缝,剩下的空格能填多少步就走多少步」。
再算最小。终局是 6 个挨着的位置。我们拿一个宽度不超过 6 的窗口在数轴上滑,窗口里已经就位的石子越多,要从外面搬进来的就越少。右指针 j 往右扩,一旦窗口宽度超过 6,左指针 i 就跟着右移。
右指针扩到位置 2,窗口宽度 1,还在 6 以内,左指针不动。
窗内已就位 1 颗,外面还有 5 颗要搬进来,本窗需要 5 步。比之前更少,最少刷新成 5。
右指针扩到位置 3,窗口宽度 2,还在 6 以内,左指针不动。
窗内已就位 2 颗,外面还有 4 颗要搬进来,本窗需要 4 步。比之前更少,最少刷新成 4。
右指针扩到位置 5,窗口宽度 4,还在 6 以内,左指针不动。
窗内已就位 3 颗,外面还有 3 颗要搬进来,本窗需要 3 步。比之前更少,最少刷新成 3。
右指针扩到位置 6,窗口宽度 5,还在 6 以内,左指针不动。
窗内已就位 4 颗,外面还有 2 颗要搬进来,本窗需要 2 步。比之前更少,最少刷新成 2。
右指针扩到位置 8,宽度一度超过 6,左指针往右移了 1 步,缩到位置 3,让窗口宽度回到 6,控制在 6 以内。
窗内已就位 4 颗,外面还有 2 颗要搬进来,本窗需要 2 步。没比当前最少 2 更优,继续滑。
右指针扩到位置 11,宽度一度超过 6,左指针往右移了 2 步,缩到位置 6,让窗口宽度回到 6,控制在 6 以内。
窗内已就位 3 颗,外面还有 3 颗要搬进来,本窗需要 3 步。没比当前最少 2 更优,继续滑。
滑完一遍,最划算的窗口已就位 4 颗,只要再搬 2 颗,最少就是 2 步。配上前面算的最大 4 步,主例答案是 [2, 4]。
单独看这个最容易错的特例:1、2、3、4 已经连成一片,只有 9 落在远处。窗口里能装下 4 颗连续石子,看着像只差 1 颗、1 步就完事,真的吗?
朴素地套公式:窗口里有 4 颗,n 是 5,5 减 4 等于 1,好像 1 步就行。问题出在这个 1 步根本走不通,下一帧看为什么。
想 1 步收尾,只能把端点 9 挪到位置 5,凑成 1、2、3、4、5。可规则要求端点挪完不能还是端点,而 5 恰好是新的最右端,这步违规。所以 1 步走不通。
正确走法要 2 步:先把最左的 1 挪到 6,变成 2、3、4、6、9;再把端点 9 挪到 5,凑成 2、3、4、5、6 连续收工。所以「n-1 已连续、只差一个空位、最后一颗在远处」这个特例,最少是 2 步,公式里要单独判它。
边界:已连续输出 0;n=3 看间隙——没有恰好长 2 的间隙就 1 步收不了尾、最少 2 步;记牢「几乎排好」的特例也算 2 步。
面试两问:最大只有两种端缝取舍;窗口宽度上限 n 来自终局的 n 个连续位置。
参考代码
from __future__ import annotationsfrom typing import *from collections import *from functools import *from itertools import *from math import *from heapq import *from bisect import *class Solution: def numMovesStonesII(self, stones: List[int]) -> List[int]: stones.sort() mi = n = len(stones) mx = max(stones[-1] - stones[1] + 1, stones[-2] - stones[0] + 1) - (n - 1) i = 0 for j, x in enumerate(stones): while x - stones[i] + 1 > n: i += 1 if j - i + 1 == n - 1 and x - stones[i] == n - 2: mi = min(mi, 2) else: mi = min(mi, n - (j - i + 1)) return [mi, mx]复杂度
- 时间:O(n log n),瓶颈是排序;之后的最大计算 O(1)、滑动窗口一遍 O(n)
- 空间:O(1) 起,只用 i、j、mi、mx 几个变量;排序内部栈 C++/Java 约 O(log n)、Python 最坏 O(n)
易错点
面试追问把动画讲成自己的话
追问最大步数为什么只看两个候选窗口?
追问为什么滑动窗口的宽度上限取 n 而不是别的?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
单字符重复子串的最大长度
LeetCode 1156 · 中等 · 沿着 双指针套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题