歌单不就是一个队列吗?所以这题直接用队列来解决!
解析
这道题描述了一个模拟特定规则的歌曲播放顺序问题,要求我们按照指定的规则,输出歌曲的实际播放顺序。这是一个典型的队列模拟问题,由于规则具有循环和动态调整的特性,适合用数据结构deque进行高效实现。下面我们逐步解析题意、算法设计以及代码实现。
题目理解
题目描述了一个特殊的播放规则:
- 开始时播放歌单的第一首歌,并将其从歌单中移除。
- 如果歌单中还有歌曲,将当前的第一首歌移到最后。
- 重复上述过程,直到歌单中没有任何歌曲。
我们可以用一个例子来进一步明确规则:
-
初始歌单:
[5, 3, 2, 1, 4] -
播放过程:
- 播放
5(移除),剩余[3, 2, 1, 4] - 将
3移到末尾,剩余[2, 1, 4, 3] - 播放
2(移除),剩余[1, 4, 3] - 将
1移到末尾,剩余[4, 3, 1] - 播放
4(移除),剩余[3, 1] - 将
3移到末尾,剩余[1, 3] - 播放
1(移除),剩余[3] - 播放
3(移除),剩余[]
- 播放
最终播放顺序为 [5, 2, 4, 1, 3]。
分析数据结构选择
上述规则中涉及两个操作:
- 移除第一首歌:需要高效地从歌单的开头删除元素。
- 将第一首歌移到末尾:需要高效地调整元素的位置。
直接用列表实现上述操作会导致频繁的元素移动,删除第一个元素的时间复杂度为 O(n),整体时间复杂度会很高。
为了优化,我们选择双端队列(deque) :
- 双端队列支持在队首和队尾高效地插入和删除,时间复杂度为 O(1)。
deque.popleft()从队首弹出元素。deque.append(x)将元素插入到队尾。
算法设计
根据规则,我们可以设计如下算法:
-
初始化一个双端队列
queue,其内容为输入歌单。 -
初始化一个空列表
result,用于记录实际播放顺序。 -
开始循环,直到
queue为空:- 将队首的歌曲弹出并添加到
result中(模拟播放)。 - 如果队列不为空,将队首的歌曲移到队尾(模拟调整位置)。
- 将队首的歌曲弹出并添加到
-
返回
result,即真实的播放顺序。
代码实现
完整代码如下:
from collections import deque
def solution(n: int, a: list) -> list:
# 使用deque来高效地进行队列操作
queue = deque(a) # 初始化双端队列
result = [] # 用于存储播放顺序
while queue: # 循环直到队列为空
# 播放当前第一首歌
result.append(queue.popleft())
# 如果队列不为空,将下一首歌移到队列的最后
if queue:
queue.append(queue.popleft())
return result
代码详解
-
初始化队列:
queue = deque(a):将输入歌单转换为双端队列。- 例如,输入
[5, 3, 2, 1, 4]转换为deque([5, 3, 2, 1, 4])。
-
模拟播放过程:
queue.popleft():移除并返回队列的第一个元素,模拟播放当前歌曲。- 结果存储到
result列表中。
-
调整队列:
queue.append(queue.popleft()):将队首元素移到队尾,模拟规则中的调整。- 例如,
deque([3, 2, 1, 4])调整后为deque([2, 1, 4, 3])。
-
循环终止条件:
- 当
queue为空时,停止循环。
- 当
-
返回结果:
result存储最终的播放顺序,作为函数输出。
时间复杂度分析
-
每次循环处理一个歌曲元素,包括:
popleft():从队首移除,时间复杂度为 O(1)。append():将元素加入队尾,时间复杂度为 O(1)。
-
对于 nn 首歌,循环 nn 次,整体时间复杂度为 O(n) 。
-
空间复杂度为 O(n) ,主要由队列和结果列表占用。