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

45 阅读3分钟

歌单不就是一个队列吗?所以这题直接用队列来解决!

解析

这道题描述了一个模拟特定规则的歌曲播放顺序问题,要求我们按照指定的规则,输出歌曲的实际播放顺序。这是一个典型的队列模拟问题,由于规则具有循环和动态调整的特性,适合用数据结构deque进行高效实现。下面我们逐步解析题意、算法设计以及代码实现。

题目理解

题目描述了一个特殊的播放规则:

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

我们可以用一个例子来进一步明确规则:

  • 初始歌单:[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]

分析数据结构选择

上述规则中涉及两个操作:

  1. 移除第一首歌:需要高效地从歌单的开头删除元素。
  2. 将第一首歌移到末尾:需要高效地调整元素的位置。

直接用列表实现上述操作会导致频繁的元素移动,删除第一个元素的时间复杂度为 O(n),整体时间复杂度会很高。

为了优化,我们选择双端队列(deque)

  • 双端队列支持在队首和队尾高效地插入和删除,时间复杂度为 O(1)。
  • deque.popleft() 从队首弹出元素。
  • deque.append(x) 将元素插入到队尾。

算法设计

根据规则,我们可以设计如下算法:

  1. 初始化一个双端队列 queue,其内容为输入歌单。

  2. 初始化一个空列表 result,用于记录实际播放顺序。

  3. 开始循环,直到 queue 为空:

    • 将队首的歌曲弹出并添加到 result 中(模拟播放)。
    • 如果队列不为空,将队首的歌曲移到队尾(模拟调整位置)。
  4. 返回 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

代码详解

  1. 初始化队列

    • queue = deque(a):将输入歌单转换为双端队列。
    • 例如,输入 [5, 3, 2, 1, 4] 转换为 deque([5, 3, 2, 1, 4])
  2. 模拟播放过程

    • queue.popleft():移除并返回队列的第一个元素,模拟播放当前歌曲。
    • 结果存储到 result 列表中。
  3. 调整队列

    • queue.append(queue.popleft()):将队首元素移到队尾,模拟规则中的调整。
    • 例如,deque([3, 2, 1, 4]) 调整后为 deque([2, 1, 4, 3])
  4. 循环终止条件

    • queue 为空时,停止循环。
  5. 返回结果

    • result 存储最终的播放顺序,作为函数输出。

时间复杂度分析

  1. 每次循环处理一个歌曲元素,包括:

    • popleft():从队首移除,时间复杂度为 O(1)。
    • append():将元素加入队尾,时间复杂度为 O(1)。
  2. 对于 nn 首歌,循环 nn 次,整体时间复杂度为 O(n)

  3. 空间复杂度为 O(n) ,主要由队列和结果列表占用。