开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第32天,点击查看活动详情
一、题目描述:
1040. 移动石子直到连续 II - 力扣(LeetCode)
在一个长度 无限 的数轴上,第 i
颗石子的位置为 stones[i]
。如果一颗石子的位置最小/最大,那么该石子被称作 端点石子 。
每个回合,你可以将一颗端点石子拿起并移动到一个未占用的位置,使得该石子不再是一颗端点石子。
值得注意的是,如果石子像 stones = [1,2,5]
这样,你将 无法 移动位于位置 5 的端点石子,因为无论将它移动到任何位置(例如 0 或 3),该石子都仍然会是端点石子。
当你无法进行任何移动时,即,这些石子的位置连续时,游戏结束。
要使游戏结束,你可以执行的最小和最大移动次数分别是多少? 以长度为 2 的数组形式返回答案:answer = [minimum_moves, maximum_moves]
。
示例 1:
输入:[7,4,9]
输出:[1,2]
解释:
我们可以移动一次,4 -> 8,游戏结束。
或者,我们可以移动两次 9 -> 5,4 -> 6,游戏结束。
示例 2:
输入:[6,5,4,3,10]
输出:[2,3]
解释:
我们可以移动 3 -> 8,接着是 10 -> 7,游戏结束。
或者,我们可以移动 3 -> 7, 4 -> 8, 5 -> 9,游戏结束。
注意,我们无法进行 10 -> 2 这样的移动来结束游戏,因为这是不合要求的移动。
示例 3:
输入:[100,101,104,102,103]
输出:[0,0]
提示:
- 3 <= stones.length <= 10^4
- 1 <= stones[i] <= 10^9
- stones[i] 的值各不相同。
二、思路分析:
最大值
第一个石头移到第二个石头的后面的第一个空位上 重复执行以上操作 直到所有石头连在一起
移动的位数 就等于第一个石头到最后一个石头之间的空位数
还有一种可能就是从右到左 倒数第一个石头移到倒数第二个石头的前面的第一个空位上
最小值
一个长度为n的滑动窗口 用来表示石头的最终位置
为了尽可能少的移动 则滑动窗口在初始时应该尽可能多的放石头
因此第一个石头必须在滑动窗口的第一个位置
然后找到滑动窗口的空位 就知道了移动的步数
有一种特殊情况 就是在滑动窗口初始化时出现连续n-1个石头 但不是连续n个石头时
不需要计算 因为这个滑动窗口无法被填满 最优解不会是以这个石头为起点
如 1235 最优解肯定不会以1开头
如 1345 最优解肯定不会以3开头
三、AC 代码:
var numMovesStonesII = function (stones) {
stones.sort((a, b) => a - b);
let len = stones.length - 1;
let min = len;
for (let i = 0; i <= len; ++i) {
let tempI = i + 1;
while (tempI <= len && stones[tempI] - stones[i] <= len) {
++tempI
}
if (tempI - 1 - i === len - 1 && stones[tempI - 1] - stones[i] === len - 1) {
continue;
}
min = Math.min(min, len + 1 + i - tempI);
if (tempI === len) {
break;
}
}
return [min, Math.max(stones[len] - stones[1], stones[len - 1] - stones[0]) + 1 - len]
};