解题思路
问题理解
题目描述了一个特殊的随机播放规则:
- 首先播放歌单中的第一首歌,并将其从歌单中移除。
- 如果歌单中还有歌曲,则将当前第一首歌移到最后一首。
- 重复上述过程,直到歌单中没有任何歌曲。
数据结构选择
为了模拟这个过程,我们可以使用一个队列(Queue)来存储歌单中的歌曲。队列的特点是先进先出(FIFO),非常适合用来模拟歌曲的播放顺序。
算法步骤
- 初始化队列:将所有歌曲按顺序加入队列。
- 播放歌曲:从队列中取出第一首歌,并将其加入结果数组。
- 移动歌曲:如果队列中还有歌曲,将当前第一首歌移到队列的末尾。
- 重复:重复步骤2和步骤3,直到队列为空。
总结
通过使用队列来模拟歌曲的播放顺序,我们可以轻松地实现题目要求的播放规则。队列的特性使得我们可以方便地进行歌曲的移除和移动操作。
代码
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
public class Main {
public static int[] solution(int n, int[] a) {
// 使用队列来模拟播放顺序
Queue<Integer> queue = new LinkedList<>();
for (int song : a) {
queue.add(song);
}
int[] result = new int[n];
int index = 0;
while (!queue.isEmpty()) {
// 播放当前第一首歌
result[index++] = queue.poll();
if (!queue.isEmpty()) {
// 将当前第一首歌移到最后一首
queue.add(queue.poll());
}
}
return result;
}
public static void main(String[] args) {
System.out.println(Arrays.equals(solution(5, new int[]{5, 3, 2, 1, 4}), new int[]{5, 2, 4, 1, 3}));
System.out.println(Arrays.equals(solution(4, new int[]{4, 1, 3, 2}), new int[]{4, 3, 1, 2}));
System.out.println(Arrays.equals(solution(6, new int[]{1, 2, 3, 4, 5, 6}), new int[]{1, 3, 5, 2, 6, 4}));
}
}
代码优化
要优化这个算法的时间复杂度,我们可以从以下几个方面入手:
当前算法的时间复杂度分析
当前的算法使用了一个队列来模拟歌曲的播放顺序。每次播放一首歌后,如果队列中还有歌曲,则将当前第一首歌移到队列的末尾。这个操作的时间复杂度是O(1),因为队列的poll和add操作都是常数时间复杂度。
整个算法的时间复杂度是O(n),其中n是歌曲的数量。因为我们需要遍历所有的歌曲,并且每个歌曲最多被处理两次(一次播放,一次移动)。
优化思路
- 减少不必要的操作:当前的算法已经非常高效,因为它直接模拟了题目描述的过程。每个歌曲最多被处理两次,因此时间复杂度已经是O(n),很难再进一步优化。
- 空间复杂度优化:虽然时间复杂度已经是最优的,但我们还可以考虑空间复杂度的优化。当前的算法使用了额外的队列来存储歌曲,这需要O(n)的空间。如果我们能直接在原数组上进行操作,可能会减少空间的使用。
虽然时间复杂度已经是最优的,但我们可以尝试减少空间复杂度的使用。我们可以直接在原数组上进行操作,而不使用额外的队列。
public class Main {
public static int[] solution(int n, int[] a) {
int[] result = new int[n];
int index = 0;
int currentIndex = 0;
for (int i = 0; i < n; i++) {
// 播放当前第一首歌
result[index++] = a[currentIndex];
// 将当前第一首歌移到最后一首
if (i < n - 1) {
currentIndex = (currentIndex + 1) % n;
while (a[currentIndex] == -1) {
currentIndex = (currentIndex + 1) % n;
}
a[currentIndex] = -1; // 标记为已播放
currentIndex = (currentIndex + 1) % n;
while (a[currentIndex] == -1) {
currentIndex = (currentIndex + 1) % n;
}
}
}
return result;
}
public static void main(String[] args) {
System.out.println(Arrays.equals(solution(5, new int[]{5, 3, 2, 1, 4}), new int[]{5, 2, 4, 1, 3}));
System.out.println(Arrays.equals(solution(4, new int[]{4, 1, 3, 2}), new int[]{4, 3, 1, 2}));
System.out.println(Arrays.equals(solution(6, new int[]{1, 2, 3, 4, 5, 6}), new int[]{1, 3, 5, 2, 6, 4}));
}
}
- 直接在原数组上操作:我们使用一个额外的数组
result来存储播放顺序,同时在原数组a上标记已播放的歌曲。 - 标记已播放的歌曲:我们将已播放的歌曲标记为
-1,这样在移动歌曲时可以跳过这些已播放的歌曲。 - 移动歌曲:在播放一首歌后,我们将当前第一首歌移到最后一首,并标记为已播放。