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

28 阅读4分钟

这道题的主要任务是模拟一个特殊的随机播放规则,根据给定的播放逻辑,还原歌曲的实际播放顺序。乍一看,问题似乎有点抽象,但仔细分析后会发现它的本质是一个典型的队列操作问题。

问题分析

题目要求我们从给定的歌单中,按如下规则获取实际的播放顺序:

  1. 播放第一首歌,然后将其从歌单中移除。
  2. 如果歌单中还有剩余歌曲,把当前的第一首歌移动到最后。
  3. 重复这个过程,直到歌单为空。

通过具体的例子来说明:

  • 对于歌单 [5, 3, 2, 1, 4],真实的播放顺序是 [5, 2, 4, 1, 3]
  • 对于歌单 [4, 1, 3, 2],真实的播放顺序是 [4, 3, 1, 2]

从这里我们可以发现,歌单的处理规则实际上可以用“队列”这种数据结构的行为来描述。

队列的基本概念

队列(Queue)是一种先进先出(FIFO,First In First Out)的数据结构。它有两个基本操作:

  1. 入队(Enqueue): 将元素添加到队列的末尾。
  2. 出队(Dequeue): 将队列的第一个元素移除并返回。

在 Python 中,队列的功能可以通过 list 来简单模拟,比如:

python
Copy code
queue = [1, 2, 3]
queue.pop(0)  # 出队,结果为 1,队列变成 [2, 3]
queue.append(4)  # 入队,队列变成 [2, 3, 4]

解决方案的核心思路

这道题的解决方案可以直接基于队列的特性来实现。具体步骤如下:

  1. 初始化一个结果列表 result,用于存储实际播放的歌曲顺序。

  2. 使用一个循环,模拟歌单的随机播放规则:

    • 每次取出当前歌单的第一首歌(对应于队列的出队操作),将其加入到结果列表中。
    • 如果歌单中还有剩余歌曲,将剩余的第一首歌移动到最后(对应于出队后再入队的操作)。
  3. 当歌单为空时,结束循环,结果列表即为真实的播放顺序。


代码实现

根据上述思路,代码如下:

python
Copy code
def solution(n: int, a: list) -> list:
    result = []
    while a:
        # 播放当前第一首歌,并将其从歌单中移除
        result.append(a.pop(0))
        # 如果歌单还有歌曲,将当前第一首歌移到最后
        if a:
            a.append(a.pop(0))
    return result

我们可以通过几个测试用例来验证代码的正确性:

python
Copy code
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])

运行后,可以看到程序能够正确地输出结果,与题目给出的播放顺序完全一致。


代码解析

1. 初始化结果列表

python
Copy code
result = []

我们需要一个空列表来记录真实的播放顺序,每次处理歌曲时,将其添加到该列表中。

2. 使用循环模拟播放逻辑

python
Copy code
while a:

while a 的意思是,当列表 a 中还有元素时,继续循环处理。这个条件保证了我们会一直处理歌单,直到它被清空。

3. 播放第一首歌

python
Copy code
result.append(a.pop(0))

这里用到了 pop(0) 方法,表示移除列表的第一个元素并返回它。这相当于队列的出队操作,同时我们将返回的歌曲 ID 添加到 result 中,记录播放顺序。

4. 移动第一首歌到最后

python
Copy code
if a:
    a.append(a.pop(0))

如果歌单中还有歌曲,则取出当前的第一首歌,并将其重新添加到列表的末尾。这一步模拟了将第一首歌移动到最后的行为。

5. 返回结果

python
Copy code
return result

当循环结束后,我们返回记录的播放顺序列表 result


时间和空间复杂度分析

时间复杂度

在每次循环中,我们对列表进行了两次操作:

  1. pop(0):移除列表的第一个元素,这在 Python 中是一个 O(n) 的操作,因为它需要将后续元素全部前移。
  2. append():将元素添加到列表末尾,这在 Python 中是一个 O(1) 的操作。

假设初始歌单有 n 首歌曲,循环会执行 n 次。因此总时间复杂度为 O(n^2)

空间复杂度

我们使用了一个额外的结果列表 result 来存储播放顺序,所需的空间为 O(n)

因此,该算法的空间复杂度为 O(n)


知识点总结

  1. 队列的基本操作: 这道题的核心是模拟队列的出队和入队操作,理解了队列的特性,就能快速构建解决方案。
  2. 列表操作的效率: 使用 list 模拟队列时,需要注意 pop(0) 的时间复杂度是 O(n),这是因为 Python 的列表是基于动态数组实现的。如果希望更高效,可以使用 collections.deque,它的 popleft() 操作是 O(1) 的。
  3. 问题建模: 通过分析题目的规则,将其转化为队列的操作问题,这是解题的关键。