问题描述
小R有一个特殊的歌曲播放规则。给定一个歌单,播放顺序如下:
- 首先播放歌单中的第一首歌,并将其从歌单中移除。
- 如果歌单中仍然有歌曲,接下来将当前第一首歌移到歌单的最后一首,继续播放。
- 这个过程会持续进行,直到歌单中的所有歌曲都播放完毕。
例如,对于歌单 [5, 3, 2, 1, 4],播放顺序应为 [5, 2, 4, 1, 3]。首先播放 5,然后将 3 移动到队列的最后,再播放 2,依此类推。
思路与分析
这个问题实际上是模拟一个特定的队列操作。我们可以将歌单视为一个队列,然后执行如下操作:
- 从队列中取出第一首歌并播放。
- 如果队列中还有其他歌曲,则将当前播放的歌曲后面的第一首移到队列的末尾。
由于涉及到队列的先进先出(FIFO)操作,这种情况特别适合使用 队列 数据结构来模拟。
解决方法
我们可以使用 collections.deque,它提供了高效的从队列两端添加和移除元素的操作。具体步骤如下:
- 初始化队列:使用
deque(a)将给定的歌单转化为队列。 - 播放过程:循环执行:
- 从队列的前端移除并播放第一首歌。
- 如果队列非空,将当前的第一首歌移到队列的末尾。
- 终止条件:当队列为空时,表示所有歌曲都已经播放完毕。
代码实现
from collections import deque
def solution(n: int, a: list) -> list:
# 初始化队列
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转换为双端队列(deque)。队列的特性使得我们能够快速地从队列的两端进行插入和删除操作。 -
queue.popleft():移除并返回队列的左端元素,也就是当前播放的歌曲。 -
queue.append(next_song):将从队列中移出的歌曲(即当前队列中的第一首歌)添加到队列的右端。 -
while queue::循环继续进行,直到队列为空,表示所有歌曲已经播放完。 -
返回结果:最终通过
result列表返回按顺序播放的所有歌曲。
测试案例
-
输入 1:
solution(n = 5, a = [5, 3, 2, 1, 4])输出:
[5, 2, 4, 1, 3]过程:
- 播放 5,队列变为
[3, 2, 1, 4]。 - 播放 2,队列变为
[1, 4, 3]。 - 播放 4,队列变为
[3, 1]。 - 播放 1,队列变为
[3]。 - 播放 3,队列为空,结束。
- 播放 5,队列变为
-
输入 2:
solution(n = 4, a = [4, 1, 3, 2])输出:
[4, 3, 1, 2]过程:
- 播放 4,队列变为
[1, 3, 2]。 - 播放 3,队列变为
[2, 1]。 - 播放 1,队列变为
[2]。 - 播放 2,队列为空,结束。
- 播放 4,队列变为
-
输入 3:
solution(n = 6, a = [1, 2, 3, 4, 5, 6])输出:
[1, 3, 5, 2, 6, 4]过程:
- 播放 1,队列变为
[2, 3, 4, 5, 6]。 - 播放 3,队列变为
[4, 5, 6, 2]。 - 播放 5,队列变为
[6, 2, 4]。 - 播放 2,队列变为
[4, 6]。 - 播放 6,队列变为
[4]。 - 播放 4,队列为空,结束。
- 播放 1,队列变为
时间复杂度分析
- 队列操作(
popleft()和append())的时间复杂度是 O(1),因为deque提供了高效的双端操作。 - 循环次数为
n(歌单的长度)。每次操作都涉及对队列的访问和修改,因此总的时间复杂度为 O(n)。
结论
该算法通过使用队列来模拟小R的播放规则,具有简单的逻辑结构和高效的时间复杂度。使用 deque 数据结构能确保每次从队列两端的操作都是常数时间,这使得算法在处理较大输入时也能保持高效。