LeetCode 189 轮转数组|3种解法拆解,从暴力到O(1)原地最优解

文章前言

今天拆解 LeetCode Hot100 经典数组题:189. 轮转数组,题目要求将数组整体向右轮转 k 个位置,不仅可以练习数组基础操作,还能掌握「原地数组翻转」经典算法技巧,面试高频原题。本文依次讲解暴力移位、临时数组、三次数组翻转三种实现方案,对比时间、空间复杂度,带你吃透最优原地解法。

一、题目原题

题目描述

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

示例 1

输入:nums = 1,2,3,4,5,6,7, k = 3

输出:5,6,7,1,2,3,4

解释:

向右轮转 1 步: 7,1,2,3,4,5,6

向右轮转 2 步: 6,7,1,2,3,4,5

向右轮转 3 步: 5,6,7,1,2,3,4

示例 2

输入:nums = -1,-100,3,99, k = 2

输出:3,99,-1,-100

进阶要求

1.尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。

2.你可以使用空间复杂度为 O(1) 的原地算法解决这个问题吗?

前置小知识点

当k > 数组长度时,旋转整数组长度次数等于数组还原,因此统一处理:k = k % nums.length,去除无效旋转。

二、解法一:暴力循环移位(直观易懂,大数据量超时)

思路

循环 k 次,每一轮只将数组最后一位元素取出,整体数组向后挪一位,最后把末尾元素放到数组头部。

Java 代码实现

java 复制代码
class Solution {
    public void rotate(int[] nums, int k) {
        int n = nums.length;
        k = k % n;
        // 循环k次,每次右移1位
        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(n×k),数组长度 n,循环 k 次移位,数组很长时直接超时

•空间复杂度:O(1),仅使用临时变量存储末尾值

优缺点

优点:逻辑直白,新手容易理解轮转逻辑;

缺点:效率极低,力扣大数据用例直接超时,工程基本不用。

三、解法二:临时数组辅助(空间换时间,刷题快速过题)

思路

新建一个等长临时数组,通过公式temp(i + k) % n = numsi计算每个元素旋转后的下标,全部存入临时数组,最后使用System.arraycopy将临时数组完整覆盖到原数组。

Java 代码实现

java 复制代码
class Solution {
    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 数组覆盖

System.arraycopy(源数组, 源起始索引, 目标数组, 目标起始索引, 复制长度)

是 Java 底层原生数组拷贝方法,性能优于 for 循环赋值,可以直接原地修改原数组内容,适配本题void无返回值要求。

复杂度分析

•时间复杂度:O(n),仅一次遍历数组

•空间复杂度:O(n),额外开辟一个数组空间

优缺点

优点:代码简洁、一次遍历完成,通过率稳定;

缺点:不符合进阶原地算法要求,额外占用内存。

四、解法三:三次数组翻转(原地最优解,面试核心考点)

核心原理

无需额外数组,依靠自定义区间反转函数完成轮转,三步走(样例1,2,3,4,5,6,7,k=3演示)

  1. 整体数组反转:7,6,5,4,3,2,1
  2. 反转前 k 个元素:5,6,7,4,3,2,1
  3. 反转后半段剩余元素:5,6,7,1,2,3,4
    自定义 reverse 反转工具方法
    双指针实现区间原地反转,输入数组 + 左右边界,直接修改数组内容

Java 代码实现

反转函数代码

java 复制代码
// 闭区间[left,right]数组反转
private void reverse(int[] nums, int left, int right) {
    while (left < right) {
        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
        left++;
        right--;
    }
}

完整代码

java 复制代码
class Solution {
    public void rotate(int[] nums, int k) {
        int len = nums.length;
        k = k % len;
        reverse(nums, 0, len - 1);  // 整体反转
        reverse(nums, 0, k - 1);    // 前k位反转
        reverse(nums, k, len - 1);  // 后半段反转
    }

    // 区间反转工具函数
    private void reverse(int[] nums, int left, int right) {
        while (left < right) {
            int temp = nums[left];
            nums[left] = nums[right];
            nums[right] = temp;
            left++;
            right--;
        }
    }
}

复杂度分析

•时间复杂度:O(n),数组每个元素仅被交换 2 次

•空间复杂度:O(1),仅临时交换变量,纯原地操作

优缺点

优点:完美满足进阶 O (1 空间要求,面试标准答案;反转函数是数组通用工具,可以复用在大量算法题;

缺点:需要理解反转逻辑,新手需要画图辅助理解。

五、三种方案横向对比表

六、核心总结

1.数组旋转预处理必做k = k % nums.length,规避超大 k 值无效运算;
2.System.arraycopy是数组覆盖赋值高效 API,注意区分数组直接赋值(只是修改引用,无法修改原数组内容);
3.双指针reverse区间反转是数组高频工具函数,必须熟练手写;
4.面试遇到数组旋转类题目,直接给出三次反转原地解法,是最优解题答案。

相关推荐
土司大王6 小时前
LeetCode hot100 回溯专题总结:Java 通用模板、决策树模型
java·算法·leetcode·决策树
2601_962218617 小时前
万象生鲜系统订单全生命周期状态同步技术实现业务可视
大数据·数据库·人工智能·python·算法
那年窗外下的雪.7 小时前
AIDC 学习日志|第 22 天|All-Active/Single-Active 故障演练设计
服务器·网络·学习·算法·哈希算法
wzdark8 小时前
基于启发式搜索的最优路径规划算法研究4
算法
动词ing8 小时前
【学习笔记】数据结构栈与队列(栈先进后出+队列先进先出+括号匹配+消消乐模型)
数据结构·笔记·学习
l1t8 小时前
用superpi、tinypi、tpi等工具计算圆周率的比较
c语言·算法
a187927218318 小时前
【算法】链表(二):链表上的双指针——变速、异链与定距,和一份路程账本
数据结构·算法·leetcode·链表·go·指针·环形链表
2601_958352909 小时前
还要写 AEC 算法?0 代码 + 6 个引脚,F-18 让通话清晰度提升 300%
人工智能·算法·降噪消回音
happyprince9 小时前
03-regmix-深刻观-哲学与升华
算法
zcmodeltech9 小时前
火电厂模型能演示冷态启动和故障处理吗?——基于STM32与Modbus RTU的集控实训控制系统设计
数据结构·stm32·单片机·嵌入式硬件·能源