小R的随机播放顺序题解 | 豆包MarsCode AI 刷题

164 阅读3分钟

问题描述

小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]

问题分析

小R的音乐播放规则如下:

  1. 从歌单中播放第一首歌,并将其从歌单中移除。
  2. 如果歌单中还有歌曲,将当前的第一首歌移到最后一首。
  3. 重复上述过程,直到歌单中没有任何歌曲。

通过样例可以观察到,播放顺序呈现出一种特定的模式,需要通过模拟这个过程来确定最终的播放顺序。

解决方案

我们可以利用 collections.deque(双端队列)来高效地实现这一过程。双端队列支持高效的从队列两端进行元素的添加和移除操作。

具体步骤如下:

  1. 初始化一个双端队列 queue,并将输入的歌单 a 转换为双端队列。

  2. 使用一个循环,直到队列为空:

    • 从队列头部弹出第一首歌,并将其添加到结果列表中。
    • 如果队列中还有歌曲,继续弹出当前的第一首歌,并将其追加到队列的尾部。
  3. 最终,结果列表 result 中存储的就是真实的播放顺序。

代码实现

from collections import deque

def solution(n, a):
    result = []
    queue = deque(a)
    while queue:
        first = queue.popleft()
        result.append(first)
        if queue:
            # 将下一个元素移动到队列的尾部,模拟下一次的“接下来”操作
            next_song = queue.popleft()
            queue.append(next_song)
    return result

**

代码解释

  1. 导入 deque

    from collections import deque
    

    **

    deque 是 Python 标准库中 collections 模块提供的一个双端队列实现,支持高效的头部和尾部操作。

  2. 初始化结果列表和队列

    result = []
    queue = deque(a)
    

    **

    将输入的歌单 a 转换为双端队列 queue,并初始化结果列表 result

  3. 循环处理队列

    while queue:
        first = queue.popleft()
        result.append(first)
        if queue:
            next_song = queue.popleft()
            queue.append(next_song)
    

    **

    • queue.popleft():从队列头部移除并返回一个元素。
    • result.append(first):将移除的元素添加到结果列表中。
    • 如果队列不为空,继续移除下一个元素,并将其追加到队列尾部。
  4. 返回结果

    return result
    

    **

    最终返回结果列表 result,其中存储了真实的播放顺序。

复杂度分析

  • 时间复杂度:O(n),其中 n 是歌曲的数量。每个歌曲最多被移除和添加到队列尾部一次。
  • 空间复杂度:O(n),需要额外的空间存储队列和结果列表。

双端队列

C++中的STL库提供了deque用于实现双端队列,Python的collections也提供了双端队列的库。

1. 定义

双端队列(Deque,全称 Double-Ended Queue)是一种特殊的队列,允许在队列的两端(前端和后端)进行插入和删除操作。双端队列结合了队列(Queue)和栈(Stack)的特性,支持从队列的前端和后端添加或删除元素。

2. 特点

  • 两端操作:可以在队列的前端(左端)和后端(右端)进行插入和删除操作。
  • 高效性:通常支持高效的插入和删除操作(O(1) 时间复杂度)。
  • 灵活性:既可以作为队列使用(FIFO,先进先出),也可以作为栈使用(LIFO,后进先出)。