石子移动问题
问题描述
小S正在玩一个关于石子的游戏,给定了一些石子,它们位于一维数轴的不同位置,位置用数组 stones 表示。如果某个石子处于最小或最大的一个位置,我们称其为端点石子。
在每个回合,小S可以将一颗端点石子移动到一个未占用的位置,使其不再是端点石子。游戏继续,直到石子的位置变得连续,无法再进行任何移动操作。
你需要帮助小S找到可以移动的最大次数。
思路
一、思路概述
该代码主要是要解决与一组石子相关的某种移动次数计算问题。整体思路是先对输入的石子数组进行排序,然后分别计算出最大移动次数和最小移动次数(虽然代码最后返回的是最大移动次数,但计算最小移动次数的过程也是完整的),通过遍历数组并结合特定条件判断来实现这些计算。
二、代码结构
1. solution方法
- 输入参数:接受一个整数数组
stones,代表一组石子的某种属性值(可能是位置或其他相关量)。 - 返回值:返回一个整数,表示计算得出的最大移动次数。
- 内部逻辑步骤: - 首先判断输入数组的长度,如果长度为
1,则直接返回0,因为只有一个石子不存在移动的概念。 - 接着使用Arrays.sort(stones)对输入的石子数组进行排序,以便后续基于排序后的顺序进行计算。 -
- 计算最大移动次数:通过
int maxMoves = Math.max(stones[n - 1] - stones[1], stones[n - 2] - stones[0]) - (n - 2);计算。这里是比较数组中最后一个石子与第二个石子的差值,以及倒数第二个石子与第一个石子的差值,取较大值后再减去(n - 2),其中n是数组的长度,这样得出的结果就是最大移动次数的一种计算方式(具体含义可能与石子的排列和移动规则相关)。
- 计算最大移动次数:通过
-
- 计算最小移动次数(使用滑动窗口): - 初始化
minMoves为Integer.MAX_VALUE,用于后续不断更新找到最小的移动次数。 - 通过一个外层循环for (int i = 0; i < n; i++)遍历排序后的石子数组。 - 在内层有一个while循环while (j < n && stones[j] - stones[i] + 1 <= n),用于控制一个滑动窗口的移动。这个窗口的起始位置由外层循环的i确定,结束位置由j确定,目的是确保窗口内最多有n个石子(通过stones[j] - stones[i] + 1 <= n这个条件来保证)。 - 在每次滑动窗口移动后,计算当前窗口内已经在合适位置的石子数量alreadyInPlace = j - i。 - 如果alreadyInPlace == n - 1且stones[j - 1] - stones[i] + 1 == n - 1,说明遇到了一种特殊情况,此时将minMoves更新为Math.min(minMoves, 2)。 - 否则,将minMoves更新为Math.min(minMoves, n - alreadyInPlace),这是一般情况下计算最小移动次数的方式。
- 计算最小移动次数(使用滑动窗口): - 初始化
-
2.
main方法 - 在main方法中,通过调用solution方法并传入不同的测试数组new int[]{7, 4, 9}、new int[]{6, 5, 4, 3, 10}、new int[]{1, 2, 3, 4, 5}等来验证solution方法的计算结果是否符合预期,通过System.out.println(solution(new int[]{7, 4, 9}) == 2)等类似语句输出验证结果。
总结
首先,处理边界情况,若数组长度为 1 则直接返回 0,因单石子不存在移动概念。
接着对数组排序以便后续计算。计算最大移动次数时,先算出数组最后一个石子与第二个石子的差值、倒数第二个石子与第一个石子的差值,取两者最大值再减去数组长度减 2 的值。
计算最小移动次数采用滑动窗口法。初始化最小移动次数为极大值,通过外层for循环遍历数组确定窗口起始位置,内层while循环依条件控制窗口结束位置以保证窗口内石子数合理。每次移动窗口后,根据窗口内石子数量判断特殊情况并更新最小移动次数,特殊情况按特定规则处理,一般情况则用数组长度减去窗口内石子数量来更新,最终返回最大移动次数。