石子移动问题解析
问题描述
小S正在玩一个关于石子的游戏。给定一些石子,它们位于一维数轴的不同位置,位置用数组 stones 表示。如果某个石子处于最小或最大的一个位置,我们称其为端点石子。在每个回合,小S可以将一颗端点石子移动到一个未占用的位置,使其不再是端点石子。游戏继续,直到石子的位置变得连续,无法再进行任何移动操作。你需要帮助小S找到可以移动的最大次数。
难度:中
核心问题
核心问题是通过移动端点石子,使得所有石子的位置变得连续,即不存在任何两个相邻石子之间有空位。目标是计算达到这一目标所需的最大移动次数。
解题思路
- 排序:首先对石子的位置数组进行排序,以便于后续操作。
- 计算跨度:定义一个辅助函数
ll(s)来计算当前石子分布的最大跨度,即最大位置与最小位置之间的差值加一。 - 贪心策略:由于最终目标是计算最大移动次数,因此每次选择能够产生更大影响的端点石子进行移动。具体来说,比较去除第一个石子后与去除最后一个石子后的剩余石子最大跨度,选择导致跨度更大的那一个端点石子进行移动。
- 插入新石子:在距离端点最近的 “空位置” 插入新的石子,以填补空隙。
- 终止条件:当所有石子的位置变得连续时,即当前的跨度等于石子的数量时,停止移动。
主要逻辑
- 初始化:对石子位置数组进行排序,并初始化移动次数
number为0。 - 循环移动:在当前石子分布的最大跨度大于石子数量时,继续移动石子。
- 计算去除第一个石子后和去除最后一个石子后的剩余石子最大跨度。
- 选择导致跨度更大的端点石子进行移动。
- 在适当的位置插入新的石子,以填补空隙。
- 更新移动次数
number。
- 返回结果:当所有石子的位置变得连续时,返回移动次数
number。
代码实现
def solution(stones: list) -> int:
# 对石子位置数组进行排序
sort_list = sorted(stones)
number = 0
# 定义辅助函数,计算当前石子分布的最大跨度
def ll(s):
return max(s) - min(s) + 1
# 循环移动石子,直到所有石子的位置变得连续
while ll(sort_list) > len(stones):
if ll(sort_list[1:]) >= ll(sort_list[:-1]):
# 移动第一个石子
sort_list.pop(0)
for i in range(len(sort_list) - 1):
if sort_list[i] + 1 < sort_list[i + 1]:
sort_list.insert(i + 1, sort_list[i] + 1)
number += 1
else:
# 移动最后一个石子
sort_list.pop(-1)
for i in range(len(sort_list) - 1, -1, -1):
if sort_list[i] - 1 > sort_list[i - 1]:
sort_list.insert(i, sort_list[i] - 1)
number += 1
return number
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)
复杂度分析
-
时间复杂度:
- 排序操作的时间复杂度为 (O(n \log n))。
- 主要循环的时间复杂度为 (O(n^2)),因为在每次移动操作中,最坏情况下需要遍历整个数组来查找插入位置。
- 总的时间复杂度为 (O(n^2))。
-
空间复杂度:
- 排序操作需要 (O(n)) 的额外空间。
- 其他变量占用的空间是常数级别的,所以总体空间复杂度为 (O(n))。