青训营X豆包MarsCode 技术训练AI刷题小R的随机播放顺序 | 豆包MarsCode AI 刷题

106 阅读4分钟

解题思路

问题理解

题目描述了一个特殊的随机播放规则:

  1. 首先播放歌单中的第一首歌,并将其从歌单中移除。
  2. 如果歌单中还有歌曲,则将当前第一首歌移到最后一首。
  3. 重复上述过程,直到歌单中没有任何歌曲。

数据结构选择

为了模拟这个过程,我们可以使用一个队列(Queue)来存储歌单中的歌曲。队列的特点是先进先出(FIFO),非常适合用来模拟歌曲的播放顺序。

算法步骤

  1. 初始化队列:将所有歌曲按顺序加入队列。
  2. 播放歌曲:从队列中取出第一首歌,并将其加入结果数组。
  3. 移动歌曲:如果队列中还有歌曲,将当前第一首歌移到队列的末尾。
  4. 重复:重复步骤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),因为队列的polladd操作都是常数时间复杂度。

整个算法的时间复杂度是O(n),其中n是歌曲的数量。因为我们需要遍历所有的歌曲,并且每个歌曲最多被处理两次(一次播放,一次移动)。

优化思路

  1. 减少不必要的操作:当前的算法已经非常高效,因为它直接模拟了题目描述的过程。每个歌曲最多被处理两次,因此时间复杂度已经是O(n),很难再进一步优化。
  2. 空间复杂度优化:虽然时间复杂度已经是最优的,但我们还可以考虑空间复杂度的优化。当前的算法使用了额外的队列来存储歌曲,这需要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}));
    }
}
  1. 直接在原数组上操作:我们使用一个额外的数组result来存储播放顺序,同时在原数组a上标记已播放的歌曲。
  2. 标记已播放的歌曲:我们将已播放的歌曲标记为-1,这样在移动歌曲时可以跳过这些已播放的歌曲。
  3. 移动歌曲:在播放一首歌后,我们将当前第一首歌移到最后一首,并标记为已播放。