问题描述
小R有一个特殊的随机播放规则。他首先播放歌单中的第一首歌,播放后将其从歌单中移除。如果歌单中还有歌曲,则会将当前第一首歌移到最后一首。这个过程会一直重复,直到歌单中没有任何歌曲。
例如,给定歌单 [5, 3, 2, 1, 4],真实的播放顺序是 [5, 2, 4, 1, 3]。
保证歌曲中的id两两不同。
测试样例
样例1:输入:n = 5 ,a = [5, 3, 2, 1, 4] 输出:[5, 2, 4, 1, 3]
样例2:输入:n = 4 ,a = [4, 1, 3, 2] 输出:[4, 3, 1, 2]
样例3:输入:n = 6 ,a = [1, 2, 3, 4, 5, 6] 输出:[1, 3, 5, 2, 6, 4]
题目分析
题目描述了小R播放歌单的一个特殊规则。以下是规则的详细分析:
1.播放规则:
- 小R从歌单的第一首歌开始播放,然后将其从歌单中移除。
- 如果歌单中还有剩余歌曲,小R会将当前歌单中第一首歌移动到歌单的最后一首。
- 如此反复,直到歌单中没有歌曲为止。
2.模拟过程:
通过一个例子说明操作细节:
- 给定歌单 [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]。
3.问题要求:
给定歌单的初始顺序,模拟规则,输出真实播放顺序。
解题思路
1.数据结构选择:
- 歌单的操作需要高效地“取出头部元素”和“将元素插入尾部”。
- 队列(Queue) 是最佳选择:
- 支持从队列头部删除操作(播放)。
- 支持在队列尾部插入操作(移动到末尾)。
2.解决步骤:
- 将所有歌曲顺序存入队列中。
- 反复模拟播放规则:
- 取出队列的头部元素,加入播放顺序结果。
- 如果队列非空,将新的队列头部元素移到末尾。
- 当队列为空时结束。
解题步骤
1.初始化:
- 创建一个队列 songQueue,用来存储歌单中的歌曲。
- 创建一个列表 result,存储播放顺序。
2.模拟规则:
- 取头部歌曲播放: 使用 songQueue.front() 获取队列的头部元素,表示当前播放的歌曲。
- 移除歌曲: 使用 songQueue.pop() 将头部元素移出队列。
- 记录结果: 将播放的歌曲加入 result。
- 移动剩余歌曲:
- 如果队列非空,取新的头部元素,用 songQueue.push() 将其插入队列末尾。
- 再用 songQueue.pop() 从头部移除。
3.输出结果:
- 当队列为空时,所有歌曲已播放完毕,结果保存在 result 中。
代码实现
#include <iostream>
#include <vector>
#include <queue> // 引入队列头文件
// 解题函数
std::vector<int> solution(int n, std::vector<int> a) {
std::vector<int> result; // 用于存储播放顺序
std::queue<int> songQueue; // 队列模拟歌单
// 1. 将所有歌曲放入队列中
for (int song : a) {
songQueue.push(song);
}
// 2. 模拟播放过程
while (!songQueue.empty()) {
// (1) 取出队列头部的歌曲并移除,将其加入播放结果
int currentSong = songQueue.front();
songQueue.pop();
result.push_back(currentSong);
// (2) 如果队列中还有歌曲,将新的队列头部移动到末尾
if (!songQueue.empty()) {
int nextSong = songQueue.front(); // 获取新的队列头部
songQueue.pop(); // 移除头部
songQueue.push(nextSong); // 将其移到队列尾部
}
}
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; // 1表示通过
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;
}
复杂度分析
1.时间复杂度:
- 每首歌曲从队列移出并最多移到队列尾部一次,队列操作的时间复杂度为O(1)。
- 总的时间复杂度为 O(n),其中 n 为歌曲数量。
2.空间复杂度:
- 队列存储 n 首歌曲,空间复杂度为 O(n)。
- resu1t 也存储 n 首歌曲,因此总空间复杂度为 O(n)。
总结
- 通过队列高效模拟了小R的播放规则。
- 本解法结构清晰,时间和空间效率高,适合应对此类序列模拟问题。