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

187 阅读5分钟

小R的随机播放顺序

问题概述

小R有一个特殊的随机播放规则,用于播放他歌单中的歌曲。具体规则如下:
小R首先播放歌单中的第一首歌,播放后将其从歌单中移除。
如果歌单中还有剩余的歌曲,他会将当前的第一首歌(即原本的第二首歌,因为第一首歌已被移除)移到歌单的最后一首。
这个过程会一直重复,直到歌单中没有任何歌曲为止。
举个例子,给定一个歌单 [5, 3, 2, 1, 4],小R的播放顺序会是:

第一步:播放 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]。

题目解析

代码展示

def solution(n: int, a: list) -> list:
    b = [0 for i in range(n)]
    for i in range(n):
        if not a:  # 检查a是否为空
            break
        b[i] = a[0]
        a.pop(0)
        if a:  # 如果a不为空,将第一个值移到最后
            a.append(a[0])
            a.pop(0)
    return b

思路分析

我们面临的问题是模拟一个特殊的随机播放顺序,这个顺序基于一个给定的歌单列表。播放规则是:

  1. 播放列表中的第一首歌,并将其从列表中移除。
  2. 如果列表不为空,将新的第一首歌(即原来的第二首歌,因为第一首已被移除)移动到列表的末尾。
  3. 重复这个过程直到列表为空。

基于这个思路,首先初始化输出列表,创建了一个长度为 n 的列表 b,所有元素初始化为 0。 接着循环处理歌单

  • 每次将a中的一个值转移到ba中一共有n个元素,所以通过循环遍历 n 次。
  • if not a: break 检查 a 是否为空,如果是,则退出循环。此行代码用于防止IndexError报错list index out of range
  • b[i] = a[0] 将当前 a 的第一个元素赋值给 b 的当前位置。
  • a.pop(0) 移除 a 的第一个元素。
  •  if a: 将新的第一个元素移动到末尾.

这个问题的解决方案主要依赖于对歌单的顺序操作,确保按照给定的规则进行歌曲的移除和重新排列。代码实现已经较为清晰地说明了每一步操作和列表操作的逻辑。接下来,我们可以继续分析和扩展该思路。

进一步分析

时间复杂度: 该算法的时间复杂度为 ( O(n^2) )。这是因为 pop(0) 操作本身是 ( O(n) ) 的复杂度,随着 n 的增加,每次移除 a 中的第一个元素需要移动整个列表,其代价是线性的。因此,整个过程的复杂度是 ( O(n^2) ),其中 n 是歌单的初始长度。

空间复杂度: 该算法的空间复杂度是 ( O(n) )。b 是长度为 n 的列表,因此使用了线性空间。此外,a 是输入列表,在操作中不断减少元素,空间使用相同。

改进思路

可以考虑用双端队列deque)来优化列表操作。deque 支持在两端高效地进行插入和删除操作,使得复杂度降低为 ( O(n) ):

  1. deque 代替普通列表以避免 pop(0) 的线性复杂度。
  2. deque.popleft() 是 ( O(1) ) 操作,相比 list.pop(0) 更高效。
  3. deque.append() 也是 ( O(1) ),非常适合将元素移到末尾。

改进代码

from collections import deque

def solution(n: int, a: list) -> list:
    a = deque(a)  # 将输入转换为deque
    b = []
    
    while a:
        b.append(a.popleft())  # 弹出第一个元素并记录到结果中
        if a:
            a.append(a.popleft())  # 将新的第一个元素移到末尾
    
    return b

解释改进后的代码

  1. a 转换为 deque:初始时将输入列表 a 转换为 deque,以便进行高效的插入和移除。
  2. popleft():从 a 中移除并返回第一个元素,将其记录在结果列表 b 中。
  3. append():将剩余的第一个元素移到 a 的末尾。
  4. 整体流程:继续这一过程直到 a 为空。

优化效果

这种方法改进了时间复杂度,使得操作效率更高,适合处理较大的输入列表。

个人思考与分析

在分析和实现小R随机播放问题的过程中,我感到整个过程非常有趣且具有启发性。问题本身看起来简单,但深入理解其中的逻辑,并逐步模拟每一步骤的变化,能够帮助我更直观地掌握其运作原理。写下每一步的细节,观察歌单在播放过程中如何变化,强化了我对问题的理解,同时也让我意识到可视化和逐步分解复杂问题的重要性。

选择合适的数据结构是这一问题的核心体验。初始版本使用了 Python 列表,但这种实现方式在移除第一个元素时会遇到性能瓶颈,特别是在处理较长的歌单时。通过使用双端队列(deque),代码的复杂度从 ( O(n^2) ) 降到了 ( O(n) ),这展示了数据结构在优化算法中的关键作用。这让我再次深刻体会到,当涉及到插入和删除操作时,双端队列在提升效率方面的实用性。

总体而言,这次解决小R的随机播放问题不仅巩固了我的基本编程技能,还让我进一步体会到数据结构和算法优化的价值。这种思考方式是编程中不可或缺的,它不仅帮助我完成任务,还促使我思考如何以更优雅的方式解决问题,拓展了我的编程视野和理解深度。