中等题: 小R的随机播放顺序
问题描述
小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]
解题思路
普通解法
我们可以通过在原数组上操作并标记已播放的歌曲,实现模拟随机播放过程。
解题思路
- 初始化变量:
• 使用变量 play_order 保存实际的播放顺序。
• 使用 index 记录当前播放的位置。
- 播放当前歌曲:
• 每次播放数组中 index 位置的歌曲,将其加入 play_order。
• 标记该歌曲为已播放(将其设置为 None)。
- 找到下一首未播放的歌曲:
• 如果当前播放的歌曲不是最后一首,则从当前位置 index 开始循环,跳过所有标记为 None 的元素,找到下一首未播放的歌曲。
• 如果没有未播放的歌曲,则停止循环。
- 重复步骤,直到所有歌曲都被播放。
代码实现
def solution(n: int, a: list) -> list:
play_order = [] # 记录实际播放顺序
index = 0 # 当前播放的位置
while len(play_order) < n: # 当未播放的歌曲还存在时
# 播放当前歌曲
play_order.append(a[index])
# 标记当前歌曲为已播放
a[index] = None
# 找到下一首未播放的歌曲
if len(play_order) < n:
# 跳过已播放的歌曲
while a[index] is None:
index = (index + 1) % n
# 移动到下一首未播放的歌曲
index = (index + 1) % n
while a[index] is None:
index = (index + 1) % n
return play_order
复杂度分析
- 时间复杂度:
• 每次找到下一首未播放的歌曲时,可能需要跳过 n 首歌曲,因此最坏情况下的时间复杂度为 O(n²) 。
- 空间复杂度:
• 由于直接在输入数组 a 上标记已播放的歌曲,未引入额外的数据结构,因此空间复杂度为 O(1) 。
优化解法
从第一首歌开始播放,播放后就将其移除,然后将之后的第一首歌移到最后。我们很自然的可以想到使用双端队列来进行操作。方便从前面出队 也方便将元素移到末尾
解题思路
-
首先将歌单中的歌曲加载到双端队列中。
-
每次从队列中移除第一个元素(首歌)并将其添加到播放顺序列表中。
-
如果队列中还有剩余歌曲,则将当前的第一个元素移到队列末尾。
-
重复上述步骤,直到队列为空。
代码实现
from collections import deque
def solution(n: int, a: list) -> list:
# 使用 deque 作为队列
queue = deque(a)
play_order = []
while queue:
# 播放当前第一首歌
play_order.append(queue.popleft())
# 如果队列中还有歌曲,将当前第一首歌移到最后
if queue:
queue.append(queue.popleft())
return play_order
复杂度分析
- 时间复杂度:每次操作只涉及移除队首和添加到队尾,单次操作为 O(1)。对于 n 首歌,总共有 O(n) 次操作。
- 空间复杂度:使用了一个双端队列来存储歌单,空间复杂度为 O(n)。