问题描述
小R有一个特殊的随机播放规则。他首先播放歌单中的第一首歌,播放后将其从歌单中移除。如果歌单中还有歌曲,则会将当前第一首歌移到最后一首。这个过程会一直重复,直到歌单中没有任何歌曲。
例如,给定歌单 [5, 3, 2, 1, 4],真实的播放顺序是 [5, 2, 4, 1, 3]。
保证歌曲中的id两两不同。
测试样例
样例1:
输入:
n = 5 ,a = [5, 3, 2, 1, 4]
输出:[5, 2, 4, 1, 3]
样例2:
输入:
n = 4 ,a = [4, 1, 3, 2]
输出:[4, 3, 1, 2]
样例3:
输入:
n = 6 ,a = [1, 2, 3, 4, 5, 6]
输出:[1, 3, 5, 2, 6, 4]
举例说明
样例1
输入:
n = 5 ,a = [5, 3, 2, 1, 4]
输出:[5, 2, 4, 1, 3]
操作过程如下:
-
初始歌单:
[5, 3, 2, 1, 4]- 播放
5,移除后歌单变为[3, 2, 1, 4] - 将
3移到末尾,歌单变为[2, 1, 4, 3]
- 播放
-
当前歌单:
[2, 1, 4, 3]- 播放
2,移除后歌单变为[1, 4, 3] - 将
1移到末尾,歌单变为[4, 3, 1]
- 播放
-
当前歌单:
[4, 3, 1]- 播放
4,移除后歌单变为[3, 1] - 将
3移到末尾,歌单变为[1, 3]
- 播放
-
当前歌单:
[1, 3]- 播放
1,移除后歌单变为[3] - 将
3移到末尾,歌单变为[3]
- 播放
-
当前歌单:
[3]- 播放
3,移除后歌单为空。
- 播放
真实的播放顺序是:[5, 2, 4, 1, 3]
解题思路
小R的规则本质上是基于队列(Queue)结构设计的,因此可以通过队列来实现这种操作。队列的特性是“先进先出”(FIFO),正好符合“播放第一首歌并移除”的要求。而为了完成“将第一首歌移到最后”的操作,我们可以将弹出的元素重新插入到队列的末尾。
实现方法
我们可以通过Python的 collections.deque 数据结构来实现队列操作。deque 支持在队列头部和尾部高效地插入和删除元素,非常适合用来模拟小R的播放规则。
以下是具体实现步骤:
-
初始化队列: 将输入数组
a转换为deque,便于使用队列操作。 -
模拟播放规则:
- 使用
popleft()播放并移除队列的第一个元素。 - 如果队列中还有元素,则将
popleft()后的第一个元素追加到队列的尾部。
- 使用
-
重复操作: 持续执行上述步骤,直到队列为空。
-
记录播放顺序: 每次播放后,将歌曲ID存入结果列表
result。 -
返回结果: 最后输出
result,即为真实的播放顺序。
代码实现
以下是问题的完整代码实现:
from collections import deque
def solution(n, a):
queue = deque(a)
result = []
while queue:
result.append(queue.popleft())
if queue:
queue.append(queue.popleft())
return result
if __name__ == '__main__':
print(solution(n = 5, a = [5, 3, 2, 1, 4]) == [5, 2, 4, 1, 3])
print(solution(n = 4, a = [4, 1, 3, 2]) == [4, 3, 1, 2])
print(solution(n = 6, a = [1, 2, 3, 4, 5, 6]) == [1, 3, 5, 2, 6, 4])
代码解析
-
初始化队列:
deque(a)将数组a转换为双端队列,便于高效地执行首尾操作。
-
模拟播放规则:
queue.popleft()从队列左侧移除第一个元素,并将其添加到播放顺序中。- 如果队列非空,则执行
queue.append(queue.popleft()),将新的第一个元素移动到队列的尾部。
-
结果存储: 每次播放的歌曲ID被依次存入列表
result中,最终形成真实的播放顺序。
时间与空间复杂度分析
时间复杂度
- 每次
popleft()和append()操作的时间复杂度为 O(1)。 - 总共需要处理 n 首歌,每首歌最多被操作两次,因此时间复杂度为 O(n)。
空间复杂度
- 使用了一个队列来存储当前歌单,其最大长度为 n,因此空间复杂度为 O(n)。
总结
小R的随机播放顺序问题展示了如何利用队列来解决一个动态序列处理问题。通过 deque 数据结构的高效特性,我们得以快速模拟规则,生成目标播放顺序。