【除夕篇】LeetCode 热题 100 之 189.轮转数组

首先祝大家

新的一年旺旺旺!!!

核心思路

向右轮转 k 个位置,等价于将数组的后 k 个元素整体移动到数组头部。需要注意:

  • k >= nums.length,可先对 k 取模(k = k % nums.length),避免无效操作。
  • 进阶要求:实现至少三种解法,并给出空间复杂度为 O(1) 的原地算法。

解法一:使用额外数组 空间复杂度 O (n)

思路:创建一个新数组,将原数组元素按轮转后的位置填充,再复制回原数组。

  • 时间复杂度:O (n)

  • 空间复杂度:O (n)

    public void rotate(int[] nums, int k) {
    int n = nums.length;
    k = k % n;
    int[] temp = new int[n];
    for (int i = 0; i < n; i++) {
    temp[(i + k) % n] = nums[i];
    }
    System.arraycopy(temp, 0, nums, 0, n);
    }

System.arraycopy(temp, 0, nums, 0, n);

  • 作用 :将临时数组 temp 中存储的 "轮转后元素",完整复制到原数组 nums 中,实现原数组的修改。
  • 为什么不用 nums = temp :Java 中数组是引用类型 ,如果直接写 nums = temp,只是让 nums 变量指向 temp 的内存地址,而原数组的内存空间不会被修改 (如果 nums 是方法参数,外部调用者看不到变化);而 System.arraycopy 是把 temp 的内容 "复制" 到 nums 原本的内存空间,真正实现 "原地修改"。
  • 参数解释System.arraycopy(源数组, 源数组起始索引, 目标数组, 目标数组起始索引, 复制元素个数)
    • temp:源数组(轮转后的数组)
    • 0:从 temp 的索引 0 开始复制
    • nums:目标数组(要修改的原数组)
    • 0:复制到 nums 的索引 0 位置
    • n:复制 n 个元素(整个数组)

解法二:暴力轮转 空间复杂度 O (1)

思路 :每次将最后一个元素移动到数组开头,重复 k 次。

  • 时间复杂度:O (n*k)(k 较大时易超时)

  • 空间复杂度:O (1)

    public void rotate(int[] nums, int k) {
    int n = nums.length;
    k = k % n;
    for (int i = 0; i < k; i++) {
    int last = nums[n - 1];
    for (int j = n - 1; j > 0; j--) {
    nums[j] = nums[j - 1];
    }
    nums[0] = last;
    }
    }

解法三:三次反转法 空间复杂度 O (1)

思路:通过三次反转实现原地轮转,是满足进阶要求的最优解法:

  1. 反转整个数组
  2. 反转前 k 个元素
  3. 反转后 n-k 个元素
  • 时间复杂度:O (n)
  • 空间复杂度:O (1)

原理示例 :原数组:[1,2,3,4,5,6,7]k=3

  1. 反转整个数组 → [7,6,5,4,3,2,1]

  2. 反转前 3 个 → [5,6,7,4,3,2,1]

  3. 反转后 4 个 → [5,6,7,1,2,3,4]

    public void rotate(int[] nums, int k) {
    // 步骤1:获取数组长度,记为n(后续所有操作的基础)
    int n = nums.length;
    // 步骤2:计算有效轮转次数,处理k大于数组长度的情况
    k = k % n;
    // 步骤3:第一次反转:反转整个数组(索引0到n-1)
    reverse(nums, 0, n - 1);
    // 步骤4:第二次反转:反转前k个元素(索引0到k-1)
    reverse(nums, 0, k - 1);
    // 步骤5:第三次反转:反转剩余的n-k个元素(索引k到n-1)
    reverse(nums, k, n - 1);
    }

    private void reverse(int[] nums, int start, int end) {
    // 双指针循环:当start < end时,交换首尾元素并向中间靠拢
    while (start < end) {
    // 临时变量保存start位置的元素(避免交换时被覆盖)
    int temp = nums[start];
    // 把end位置的元素放到start位置
    nums[start] = nums[end];
    // 把原来start的元素放到end位置
    nums[end] = temp;
    // start指针右移一位,向中间靠近
    start++;
    // end指针左移一位,向中间靠近
    end--;
    }

用完整例子验证执行过程

以经典案例 nums = [1,2,3,4,5,6,7]k=3 为例,一步步看执行效果:

步骤 操作 数组状态
初始 - [1,2,3,4,5,6,7]
步骤 1 n=7,k=3%7=3 不变
步骤 2 反转整个数组(0-6) [7,6,5,4,3,2,1]
步骤 3 反转前 3 个元素(0-2) [5,6,7,4,3,2,1]
步骤 4 反转后 4 个元素(3-6) [5,6,7,1,2,3,4]

解法对比

解法 空间复杂度 时间复杂度 优点 缺点
额外数组 O(n) O(n) 简单易懂,效率稳定 需额外空间,不符合进阶要求
暴力轮转 O(1) O(n*k) 空间复杂度低 k 较大时易超时
三次反转 O(1) O(n) 空间复杂度低,效率最优 需要理解反转逻辑

官方题解

环状替换
复制代码
class Solution {
    public void rotate(int[] nums, int k) {
        // 步骤1:获取数组长度
        int n = nums.length;
        // 步骤2:计算有效轮转次数(避免k>n)
        k = k % n;
        // 步骤3:计算k和n的最大公约数(环的数量)
        int count = gcd(k, n);
        // 步骤4:遍历每个环的起始位置(共count个环)
        for (int start = 0; start < count; ++start) {
            // 步骤5:初始化当前位置、前一个元素(环的起点元素)
            int current = start;
            int prev = nums[start];
            // 步骤6:循环处理当前环的所有元素,直到回到起点
            do {
                // 计算当前元素轮转后的下一个位置
                int next = (current + k) % n;
                // 保存下一个位置的原始值(避免被覆盖)
                int temp = nums[next];
                // 把前一个元素放到下一个位置(完成一次替换)
                nums[next] = prev;
                // 更新前一个元素为下一个位置的原始值
                prev = temp;
                // 更新当前位置为下一个位置
                current = next;
            } while (start != current); // 回到环的起点时终止
        }
    }

    // 辅助函数:计算两个数的最大公约数(欧几里得算法)
    public int gcd(int x, int y) {
        return y > 0 ? gcd(y, x % y) : x;
    }
}

链接:https://leetcode.cn/problems/rotate-array/solutions/551039/xuan-zhuan-shu-zu-by-leetcode-solution-nipk/

来源:力扣(LeetCode) 著作权归作者所有。

相关推荐
xiaoye-duck1 小时前
《算法题讲解指南:优选算法-滑动窗口》--13 水果成篮
c++·算法
wefg11 小时前
【算法】模运算的技巧
算法
智者知已应修善业1 小时前
【冰雹猜想过程逆序输出】2025-4-19
c语言·c++·经验分享·笔记·算法
七夜zippoe1 小时前
告别SQL恐惧症:我用飞算JavaAI的SQL Chat,把数据库变成了“聊天室”
java·数据库·sql·ai·javaai
编程小白_澄映2 小时前
机器学习——特征工程
人工智能·算法·机器学习
心本无晴.2 小时前
RAG检索优化:文本分块策略如何大幅提升检索准确度
java·linux·服务器
美好的事情能不能发生在我身上2 小时前
Leetcode热题100中的:哈希专题
算法·leetcode·哈希算法
西门吹雪分身2 小时前
K8S之Ingress
java·容器·kubernetes·k8s