题目描述
小S正在玩一个关于石子的游戏,给定了一些石子,它们位于一维数轴的不同位置,位置用数组 stones 表示。如果某个石子处于最小或最大的一个位置,我们称其为端点石子。
在每个回合,小S可以将一颗端点石子移动到一个未占用的位置,使其不再是端点石子。游戏继续,直到石子的位置变得连续,无法再进行任何移动操作。
你需要帮助小S找到可以移动的最大次数。
测试样例
样例1
输入:stones = [7, 4, 9]
输出:2
样例2
输入:stones = [6, 5, 4, 3, 10]
输出:3
样例3
输入:stones = [1, 2, 3, 4, 5]
输出:0
思路
- 理解端点石子:端点石子是指在一维数轴上位置最小或最大的石子。可以通过移动这些端点石子来尝试使得石子的位置变得连续。
- 移动次数的计算:
- 首先,需要对石子的位置进行排序,以便于可以轻松找到最小和最大位置。
- 然后,可以计算出在当前状态下,石子之间的空位数量。需要确保石子之间的间隔是连续的,即没有空位。
- 最大移动次数的计算:
- 最大移动次数可以通过计算最远的两个端点之间的距离,并减去已经占用的石子数量来得到。具体来说,可以计算出:
maxMoves = max(stones[n - 1] - stones[1], stones[n - 2] - stones[0]) - (n - 2)- 这里,
stones[n - 1]和stones[1]是排序后的最大和次小位置,stones[n - 2]和stones[0]是次大和最小位置。
- 最小移动次数的计算:使用滑动窗口的方法来计算最小移动次数。通过维护一个窗口,可以找到在某个范围内已经存在的石子数量,并计算出需要移动的石子数量。
- 返回结果:最后,返回计算出的最大移动次数。
Java代码
import java.util.Arrays;
public class Main {
public static int solution(int[] stones) {
// write code here
if (stones.length == 1) {
return 0;
}
Arrays.sort(stones);
int n = stones.length;
// 计算最大移动次数
int maxMoves = Math.max(stones[n - 1] - stones[1], stones[n - 2] - stones[0]) - (n - 2);
// 计算最小移动次数,使用滑动窗口
int minMoves = Integer.MAX_VALUE;
int j = 0;
for (int i = 0; i < n; i++) {
// 确保窗口内最多有 n 个石子
while (j < n && stones[j] - stones[i] + 1 <= n) {
j++;
}
// 如果窗口内有 n - 1 个石子,且空位为 1,则需要特殊处理
int alreadyInPlace = j - i;
if (alreadyInPlace == n - 1 && stones[j - 1] - stones[i] + 1 == n - 1) {
minMoves = Math.min(minMoves, 2);
} else {
minMoves = Math.min(minMoves, n - alreadyInPlace);
}
}
return maxMoves;
}
public static void main(String[] args) {
System.out.println(solution(new int[]{7, 4, 9}) == 2);
System.out.println(solution(new int[]{6, 5, 4, 3, 10}) == 3);
System.out.println(solution(new int[]{1, 2, 3, 4, 5}) == 0);
}
}
复杂度分析
时间复杂度:排序的时间复杂度为 (O(nlogn))。计算最大移动次数和最小移动次数的过程是线性的,即(O(n))。因此,总的时间复杂度为 (O(nlogn))。
空间复杂度:主要使用了排序所需的额外空间,空间复杂度为 (O(1))(如果不考虑排序的空间复杂度)。 - 总的空间复杂度为 (O(1))(不考虑输入数组的空间)。