问题描述
小R有一个特殊的随机播放规则。给定一个歌单,首先播放歌单中的第一首歌,播放后将其从歌单中移除。如果歌单中还有歌曲,则会将当前播放的第一首歌移到最后一首。这个过程会一直重复,直到歌单中没有任何歌曲。
例如,给定歌单 [5, 3, 2, 1, 4],按照规则的播放顺序是 [5, 2, 4, 1, 3]。
保证歌曲中的id两两不同。
解题思路
此题本质上是对一个列表进行特定规则的操作。具体步骤可以概括为:
- 播放并移除:每次从歌单的头部播放一首歌,并将其从歌单中移除。
- 移到最后:如果歌单中还有歌曲,则将当前第一首歌移到歌单的最后。
- 重复直到空:上述操作会持续,直到歌单为空。
根据描述,首先从歌单中移除第一首歌,然后将剩余的第一首歌放到队列的末尾,直到歌单为空。因此,可以使用队列来模拟这一过程,因为队列的特性正符合"先入先出"(FIFO),适合解决这个问题。
数据结构选择
为了高效地模拟这个过程,我们可以使用一个队列(std::queue)来存储歌曲的顺序。
队列的特性是先进先出(FIFO),非常适合模拟歌曲的播放和移动操作。
具体步骤
- 初始化队列:我们将给定的歌单看作一个队列。
- 反复操作:每次从队列中移除第一个元素(播放),然后把它放到队列的末尾(如果队列不为空)。
- 输出播放顺序:我们通过一个数组收集播放的顺序,直到队列为空。
#include <iostream>
#include <vector>
#include<queue>
std::vector<int> solution(int n, std::vector<int> a) {
std::vector<int> result; // 用于存储最终的播放顺序
std::queue<int> q; // 用于模拟播放过程的队列
// 将所有歌曲ID放入队列中
for (int i = 0; i < n; ++i) {
q.push(a[i]);
}
while (!q.empty()) {
// 取出队列中的第一首歌并播放
int current = q.front();
q.pop();
result.push_back(current);
// 如果队列中还有歌曲,将当前队列的第一首歌移到队列的末尾
if (!q.empty()) {
int next = q.front();
q.pop();
q.push(next);
}
}
return result;
}
int main(){
std::vector<int> result1 = {5, 2, 4, 1, 3};
std::vector<int> result2 = {4, 3, 1, 2};
std::vector<int> result3 = {1, 3, 5, 2, 6, 4};
std::cout << (solution(5, {5, 3, 2, 1, 4}) == result1) << std::endl;
std::cout << (solution(4, {4, 1, 3, 2}) == result2) << std::endl;
std::cout << (solution(6, {1, 2, 3, 4, 5, 6}) == result3) << std::endl;
return 0;
}
代码分析
-
导入队列:使用
collections.deque来实现队列。deque是一个双端队列,支持从两端进行高效的插入和删除操作。 -
播放逻辑:
- 通过
queue.popleft()从队列中取出并播放第一首歌(等同于移除队列的第一个元素)。 - 然后通过
queue.append()将当前播放的歌移到队列的末尾。append操作将元素添加到队列的尾部。
- 通过
-
循环播放:循环的条件是队列非空,在队列为空之前持续执行播放和移歌到队尾的操作。直到队列为空,整个播放过程结束。
-
返回结果:最终返回一个列表
result,该列表记录了歌曲的播放顺序。
代码总结
- 通过使用队列数据结构,能够高效地实现歌曲的播放与重新排序。
deque提供了高效的从队头取出和从队尾添加的操作,保证了时间复杂度的优化。 - 该解法的时间复杂度为 O(n) ,其中
n是歌单的长度。每次操作中,每个元素最多会被添加和移除一次,因此总操作数为2n,即时间复杂度是线性的。 - 空间复杂度为 O(n) ,我们使用了额外的队列来存储歌单。
总结
本题的关键是模拟歌曲的播放过程,使用队列来管理歌曲的顺序是最合适的选择。队列的先进先出(FIFO)特性非常契合题目中的需求,通过合理使用 deque 数据结构,我们可以高效地实现这一过程。