【除夕篇】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) 著作权归作者所有。

相关推荐
别动我齐刘海14 小时前
ROS2 Jazzy + C++ 实战路线——进阶学习3
c++·人工智能·vscode·python·算法·机器学习·机器人
Bs_MoneyMagnet14 小时前
基于springboot+vue的非遗物质文化遗产系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·spring·毕业设计·计算机毕业设计
万物智能14 小时前
波形发生AD9833模块配置—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
java
zhangfeng113314 小时前
《从“人工适配“到“智能生成“:KernelSwift 跨国产芯片算子迁移全栈方案解读》 —— 强调范式跃迁和跨硬件属性,适合偏架构分析的写法
人工智能·算法·华为·ai编程·npu
0+11114 小时前
算法 --滑动窗口
c++·算法·leetcode
计算机毕设定制辅导-无忧学长14 小时前
《基于SpringBoot的未成年人健康知识科普平台的设计与实现》
java·开发语言·vue.js·spring boot·未成年人健康知识科普平台
Wang's Blog14 小时前
Java 项目实战: 外卖平台-数据库环境搭建与十一张表结构解析
java·服务器·redis
白远山15 小时前
上海24小时自助健身房系统软件开发实战指南
java·架构·uni-app·需求分析
泡海椒15 小时前
jquick-pdf 表格实战:动态数据 PDF 报表生成
java·开发语言·pdf
reikocao15 小时前
大场景下 Hessian 特征值放大与退化检测
算法