端点石子移动问题 | 豆包MarsCode AI刷题
问题描述
小S正在玩一个关于石子的游戏,游戏中给定了一些石子,它们位于一维数轴上的不同位置,位置用数组 stones 表示。如果某个石子处于最小或最大的一个位置,我们称其为端点石子。
在每个回合,小S可以将一颗端点石子移动到一个未占用的位置,使其不再是端点石子。值得注意的是,如果石子的位置是连续的,则游戏结束,因为没有可以进行的移动操作。
你需要帮助小S找到可以移动的最小次数。
测试样例
样例1:
输入:
stones = [7, 4, 9]输出:1
样例2:
输入:
stones = [6, 5, 4, 3, 10]输出:2
样例3:
输入:
stones = [1, 2, 3, 4, 5]输出:0
题目解析
题目解析
- 端点石子 在游戏中,位于数轴上最小或最大位置的石子称为端点石子,例如
[7, 4, 9]中的4和9是端点石子。 - 游戏规则 每回合只能将端点石子移动到一个未被占用的位置,直到所有石子的位置是连续的。
- 目标 找到将石子移动至连续位置所需的最小移动次数。
思路分析
核心思路:滑动窗口法
-
石子连续的判断 如果石子已经是连续的(即
stones[n-1] - stones[0] + 1 == n),则无需移动,返回0。 -
最小移动次数计算(滑动窗口)
- 对排序后的石子位置,维护一个滑动窗口
[i, j],其中窗口内所有石子连续。 - 在窗口内计算已经就位的石子数
rightPlace = j - i。 - 缺失的石子数即为当前需要的移动次数:
n - rightPlace。 - 特殊情况:如果窗口内石子数达到
n - 1且区间正好只差一个空位(即stones[j-1] - stones[i] + 1 == n - 1),最小移动次数为2。
- 对排序后的石子位置,维护一个滑动窗口
算法步骤
-
数组排序
- 确保石子按照数轴顺序排列。
-
滑动窗口
- 从左到右遍历每个石子的起始位置
i,保持窗口内的最大范围stones[j] - stones[i] + 1 <= n。 - 对每个窗口计算
rightPlace,更新最小移动次数。
- 从左到右遍历每个石子的起始位置
-
返回结果
- 返回计算出的最小移动次数。
Java 实现代码
import java.util.*;
public class Main {
public static int solution(int[] stones) {
if (stones.length == 1) {
return 0; // 单个石子,已经连续
}
Arrays.sort(stones); // 排序石子位置
int n = stones.length;
// 滑动窗口计算最小移动次数
int minCount = (int) 1e9;
int j = 0; // 窗口右边界
for (int i = 0; i < n; i++) {
// 扩展窗口,直到窗口内石子数大于 n
while (j<n && stones[j]-stones[i]+1<=n) {
j++;
}
int rightPlace = j - i; // 窗口内已经就位的石子数
// 检查特殊情况
if (rightPlace==n-1 && stones[j-1]-stones[i]+1 == n-1) {
minCount = Math.min(minCount, 2); // 特殊情况,最少需要两步
} else {
minCount = Math.min(minCount, n - rightPlace); // 一般情况
}
}
return minCount; // 返回最小移动次数
}
public static void main(String[] args) {
System.out.println(solution(new int[]{7, 4, 9})); // 输出: 1
System.out.println(solution(new int[]{6, 5, 4, 3, 10})); // 输出: 2
System.out.println(solution(new int[]{1, 2, 3, 4, 5})); // 输出: 0
}
}
代码解析
-
输入与初始化
stones.length == 1时,直接返回0。- 使用
Arrays.sort()对石子位置排序。
-
滑动窗口
- 遍历起始位置
i,扩展窗口右边界j,确保窗口内石子数满足连续条件stones[j] - stones[i] + 1 <= n。 - 计算当前窗口内的就位石子数
rightPlace,并更新最小移动次数。
- 遍历起始位置
-
特殊情况
- 当窗口覆盖
n-1个石子,且间隔仅差 1,则需要两步解决。
- 当窗口覆盖
复杂度分析
-
时间复杂度
- 排序:O(nlogn)
- 滑动窗口:O(n)
- 总复杂度:O(nlogn)
-
空间复杂度
- 常量空间:O(1)
总结
- 核心思想:通过滑动窗口的方式,计算当前窗口内石子数量和缺失石子数量,更新最小移动次数。
- 特殊情况处理:对于窗口覆盖
n-1且仅差一位的情况,直接返回2。 - 时间复杂度:排序和遍历的结合,效率较高,适用于中等规模数据。
功能亮点剖析:AI 刷题实践的独特价值
1. 精选真题
精选真题为刷题过程提供了高质量、典型化的题目,通过对重点题型的练习,能帮助学习者快速掌握解题思路和常用算法。例如,针对“石子游戏”这类动态规划和滑动窗口结合的经典问题,AI 会根据题目分类和难度层级提供合理的练习路径,避免无效练习。
2. 云端编辑器
云端编辑器直接在网页或软件内运行代码并验证结果,不需要繁琐的开发环境配置。对于 Java 的解决方案,直接输入代码并点击运行,立刻获得反馈。云端编译的特点包括:
- 随时测试:快速调试代码,验证每一部分逻辑。
- 多语言支持:学习者可以切换多种语言,比较不同语言的实现思路。
- 自动错误检测:对于常见错误(如数组越界、空指针),即时提示并帮助纠正。
3. 个性化题目推荐
AI 会根据学习者的练习记录,推荐适合其水平的题目。比如,如果用户熟悉排序问题和滑动窗口,但对动态规划较为陌生,系统会优先推荐难度较低、涉及动态规划思想的题目,逐步提升。
刷题实践:总结与案例分析
优势
- 高效学习:针对每道题目,AI 可以给出详细的解题思路、优化方案和复杂度分析。
- 逐步进阶:难度由浅入深,避免初学者因为难度跳跃性过大而放弃。
- 实时反馈:在代码运行失败时,AI 可以提供提示,例如指出数组未排序可能引起的问题。
- 多角度解答:对于同一问题,AI 会提供多种解决方案,如暴力法、滑动窗口优化、动态规划等,帮助学习者开阔思路。
实践案例
以下是一个使用 AI 刷题过程的分析:
题目:石子游戏最小移动次数
1. 初步解答
- 用户写出了一份初步代码,但对滑动窗口的结束条件处理不清晰,结果出错。AI 提供了即时提示,并指出“
j超出数组范围”是导致错误的关键。
2. 提供优化思路
- AI 建议改用“
stones[j - 1] - stones[i] + 1”来检测窗口范围是否满足条件,并减少了不必要的遍历操作,优化时间复杂度。
3. 比较学习
- 除了滑动窗口法,AI 提供了基于双指针的另一种解法,使学习者能够对比不同算法的实现差异。
总结
AI 刷题实践通过精选真题、智能反馈和多种解法的对比学习,帮助用户快速掌握算法精髓并提升编程能力。以“石子游戏”为例,通过云端测试和 AI 的解题引导,用户可以从零到熟练地掌握滑动窗口的应用,从而为其他类似问题的解答打下基础。