一,题目详情
1,问题描述
小S正在玩一个关于石子的游戏,给定了一些石子,它们位于一维数轴的不同位置,位置用数组 stones 表示。如果某个石子处于最小或最大的一个位置,我们称其为端点石子。
在每个回合,小S可以将一颗端点石子移动到一个未占用的位置,使其不再是端点石子。游戏继续,直到石子的位置变得连续,无法再进行任何移动操作。
你需要帮助小S找到可以移动的最大次数。
2,测试样例
样例1:
输入:stones = [7, 4, 9]
输出:2
样例2:
输入:stones = [6, 5, 4, 3, 10]
输出:3
样例3:
输入:stones = [1, 2, 3, 4, 5]
输出:0
二,解题思路
1,问题分析
我们需要找到石子可以移动的最大次数。每次移动只能移动端点石子到一个未占用的位置,使其不再是端点。游戏结束的条件是石子的位置连续。
2,算法策略
核心思想是:计算所有石子之间的间隔总和,然后减去最小的端点间隔(左端点间隔或右端点间隔),因为最终游戏结束时,端点间隔中较小的那个将被保留,而较大的那个会被消除。
具体步骤如下:
- 对石子位置进行排序。
- 计算所有相邻石子之间的间隔总和。
- 计算左端点间隔(第二个石子与第一个石子的间隔)和右端点间隔(最后一个石子与倒数第二个石子的间隔)。
- 最大移动次数为总间隔减去左端点间隔和右端点间隔中的较小值。
3,逐步推演(以样例1为例)
输入:stones = [7, 4, 9]
排序后:[4, 7, 9]
相邻间隔: 7 - 4 - 1 = 2 9 - 7 - 1 = 1 总间隔:2 + 1 = 3
左端点间隔:7 - 4 - 1 = 2 右端点间隔:9 - 7 - 1 = 1
最小端点间隔为1,所以最大移动次数为3 - 1 = 2。
三,代码实现
def solution(stones: list) -> int:
stones.sort()
n = len(stones)
if n <= 1:
return 0
sum_gaps = 0
for i in range(n - 1):
sum_gaps += stones[i + 1] - stones[i] - 1
left_gap = stones[1] - stones[0] - 1
right_gap = stones[-1] - stones[-2] - 1
return sum_gaps - min(left_gap, right_gap)
1,复杂度分析
-
时间复杂度:O(n log n)
- 主要为排序的时间复杂度
-
空间复杂度:O(1)
- 仅使用常数级额外空间
2,边界测试
if __name__ == '__main__':
# 常规测试
print(solution(stones=[7, 4, 9]) == 2)
print(solution(stones=[6, 5, 4, 3, 10]) == 3)
print(solution(stones=[1, 2, 3, 4, 5]) == 0)
# 边界测试:只有一个石子
print(solution(stones=[5]) == 0)
# 边界测试:两个石子
print(solution(stones=[1, 5]) == 3)
四,总结
通过计算所有间隔并减去最小的端点间隔,我们实现了:
- 找到最大移动次数:通过分析石子的间隔分布
- 高效的计算:利用排序和简单遍历
- 普适性:适用于所有石子分布情况
这种解法不仅高效,还易于理解和实现。当遇到“需要计算石子移动的最大次数”类问题时,分析间隔和端点特性往往是解决问题的关键策略。