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

194 阅读4分钟

问题描述

小R有一个特殊的随机播放规则。他首先播放歌单中的第一首歌,播放后将其从歌单中移除。如果歌单中还有歌曲,则会将当前第一首歌移到最后一首。这个过程会一直重复,直到歌单中没有任何歌曲。

例如,给定歌单 [5, 3, 2, 1, 4],真实的播放顺序是 [5, 2, 4, 1, 3]

保证歌曲中的id两两不同。

一、问题分析与思路

这道题目本质上是对一个歌单进行模拟操作。题目给定了一个播放规则,要求我们按照规则计算出歌曲播放的顺序。具体规则是:

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

问题的关键在于如何模拟这个播放规则。我们需要找一种方法,每次都能方便地取出歌单的第一首歌,并且能够高效地把它移到最后一位。

二、如何解决?

我在刷题时思考了几个可能的解决方案,最终选择了使用 队列 来模拟这个过程。

为什么选择队列?队列是一个先进先出的数据结构,非常符合题目要求:

  • 每次取出第一首歌,并移除它。
  • 如果歌单中还有歌曲,将当前的第一首歌放到队列的末尾。

队列 的操作是非常高效的,可以在常数时间内完成元素的插入和删除,适合我们解决这个问题。

三、步骤

为了更好理解这个过程,用一个简单的示例来帮助理清思路:

假设歌单为 [5, 3, 2, 1, 4]。

  1. 初始状态:歌单 [5, 3, 2, 1, 4]

    • 播放 5,移除 5,剩余歌单 [3, 2, 1, 4]。
    • 将 3 移到末尾,歌单变为 [2, 1, 4, 3]。
  2. 第二轮播放:歌单 [2, 1, 4, 3]

    • 播放 2,移除 2,剩余歌单 [1, 4, 3]。
    • 将 1 移到末尾,歌单变为 [4, 3, 1]。
  3. 第三轮播放:歌单 [4, 3, 1]

    • 播放 4,移除 4,剩余歌单 [3, 1]。
    • 将 3 移到末尾,歌单变为 [1, 3]。
  4. 第四轮播放:歌单 [1, 3]

    • 播放 1,移除 1,剩余歌单 [3]。
    • 将 3 移到末尾,歌单变为 [3]。
  5. 最后一轮播放:歌单 [3]

    • 播放 3,移除 3,剩余歌单 [](歌单为空)。

播放顺序为 [5, 2, 4, 1, 3]。

四、代码详解

在理解了思路和规则后,我们可以将其转化为代码。关键在于如何高效地管理队列,我们使用 collections.deque来实现队列。

代码步骤:

  1. 将输入的歌单列表 a 转换为队列 deque(a),这样我们可以方便地从队列头部取出元素并从尾部插入元素。
  2. 使用 popleft() 来取出队列的第一首歌并播放。
  3. 如果队列中还有剩余歌曲,则使用 append() 将当前的第一首歌放到队列的末尾。
  4. 直到队列为空时结束模拟。
from collections import deque

def solution(n: int, a: list) -> list:
    queue = deque(a)  # 将输入列表转为队列
    result = []
    
    while queue:
        result.append(queue.popleft())  # 播放当前第一首歌并移除
        if queue:  # 如果歌单还有歌曲
            queue.append(queue.popleft())  # 将当前的第一首歌移到最后
    
    return result

五、学习心得

通过这道题,我学到了一些重要的算法思考方式,特别是如何利用适合的数据结构来解决问题。总结起来,以下几个点对我特别有帮助:

  1. 理解题意,找出核心操作

    • 题目给定的规则是“取出第一首歌并移到最后”,这其实就是一个典型的队列操作。理解问题的核心后,我们就能找到合适的数据结构来简化操作。
  2. 避免过度复杂化

    • 一开始,我曾考虑过用 list 或其他结构,但很快意识到,deque的时间复杂度是 O(1),而 list 则可能需要 O(n) 的时间来做插入和删除操作,效率较低。