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

📅 2026/8/3 6:39:55 👁️ 阅读次数 📝 编程学习
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 代码实现

classSolution{publicvoidrotate(int[]nums,intk){intn=nums.length;k=k%n;// 循环k次,每次右移1位for(inti=0;i<k;i++){// 保存最后一个元素intlast=nums[n-1];// 数组整体后移一位for(intj=n-1;j>0;j--){nums[j]=nums[j-1];}// 头部赋值nums[0]=last;}}}

复杂度分析
•时间复杂度:O(n×k),数组长度 n,循环 k 次移位,数组很长时直接超时
•空间复杂度:O(1),仅使用临时变量存储末尾值

优缺点
优点:逻辑直白,新手容易理解轮转逻辑;
缺点:效率极低,力扣大数据用例直接超时,工程基本不用。

三、解法二:临时数组辅助(空间换时间,刷题快速过题)
思路
新建一个等长临时数组,通过公式temp[(i + k) % n] = nums[i]计算每个元素旋转后的下标,全部存入临时数组,最后使用System.arraycopy将临时数组完整覆盖到原数组。

Java 代码实现

classSolution{publicvoidrotate(int[]nums,intk){intn=nums.length;k=k%n;int[]temp=newint[n];// 填充临时数组for(inti=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 代码实现
反转函数代码

// 闭区间[left,right]数组反转privatevoidreverse(int[]nums,intleft,intright){while(left<right){inttemp=nums[left];nums[left]=nums[right];nums[right]=temp;left++;right--;}}

完整代码

classSolution{publicvoidrotate(int[]nums,intk){intlen=nums.length;k=k%len;reverse(nums,0,len-1);// 整体反转reverse(nums,0,k-1);// 前k位反转reverse(nums,k,len-1);// 后半段反转}// 区间反转工具函数privatevoidreverse(int[]nums,intleft,intright){while(left<right){inttemp=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.面试遇到数组旋转类题目,直接给出三次反转原地解法,是最优解题答案。