LeetCode 189. 轮转数组
题目描述

题意分析
给定长度为
n的数组,把每个元素整体向右挪动k位:原来下标i上的值,最终要落在下标(i + k) % n上,末尾溢出的部分绕回开头。题目要求「原地修改」,也就是不能把答案装进另一个数组再返回。约束里有两个信号。第一,
k只保证非负,并不保证小于n,因此k完全可能比数组还长,必须先把它和n的关系理清楚;第二,进阶明确点名了额外空间 $O(1)$ 的做法,这说明「开一个等长辅助数组抄一遍」虽然能过,却不是这道题真正想考的写法。边界集中在三处:
k为0或恰好是n的倍数时,数组必须原封不动;n为1时任何k都不改变结果;数组为空时不能对长度做取模,否则直接除零。
解法:三次反转
核心思路
问题关键:右移
k位等价于把数组切成A = nums[0..n-k-1]、B = nums[n-k..n-1],再把A + B变成B + A,同时保持两段内部顺序。为什么选三次反转:辅助数组能在 $O(n)$ 时间完成,但需要 $O(n)$ 空间;环状替换虽是原地算法,却要处理多条置换环。三次反转只需一个交换变量,白板实现最短且边界清晰:
reverse(A + B) = reverse(B) + reverse(A),再分别反转两段,就得到B + A。过程不变量:整体反转后,前
k个位置恰好是B的逆序,剩余位置恰好是A的逆序;后两次反转只恢复各段内部顺序,不再改变两段位置。正确性:第一次反转已经交换了
A、B的相对位置;第二、三次反转分别抵消它们各自的逆序。因此每个元素都落到右移后的唯一位置,且全程只交换元素,不会丢失或重复。
解题步骤
- 空数组直接返回,避免随后对 0 取模。
- 执行
k %= n,把任意位移归一化到[0,n-1]。- 反转整个数组,再反转
[0,k-1],最后反转[k,n-1]。口述样例:
[1,2,3,4,5,6,7]右移 3 位:整体反转为[7,6,5,4,3,2,1],再恢复前 3 位和后 4 位,得到[5,6,7,1,2,3,4]。
代码实现
class Solution {
public void rotate(int[] nums, int k) {
if (nums.length == 0) {
return;
}
k %= nums.length;
reverse(nums, 0, nums.length - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.length - 1);
}
private void reverse(int[] nums, int left, int right) {
while (left < right) {
int value = nums[left];
nums[left] = nums[right];
nums[right] = value;
left++;
right--;
}
}
}
func rotate(nums []int, k int) {
if len(nums) == 0 {
return
}
k %= len(nums)
reverse(nums, 0, len(nums)-1)
reverse(nums, 0, k-1)
reverse(nums, k, len(nums)-1)
}
func reverse(nums []int, left int, right int) {
for left < right {
nums[left], nums[right] = nums[right], nums[left]
left++
right--
}
}
复杂度分析
- 时间复杂度:$O(n)$。三个反转区间的总长度为
2n。- 空间复杂度:$O(1)$,只使用下标和交换临时变量。
关键点总结
- 循环位移先转化为相邻块交换,再选择原地反转实现。
k %= n既处理超大k,也保证两个子区间合法。reverse使用闭区间[left,right],调用处必须保持同一约定。- 若追问另一种 $O(1)$ 空间解,可说明环状替换需要处理
gcd(n,k)条置换环,代码更易错。
易错点总结
- 必须先判空再做
k %= nums.length,否则空数组会触发除零。- 忘记取模时,
nums = [1,2]、k = 3会产生非法反转区间。- 三个闭区间应为
[0,n-1]、[0,k-1]、[k,n-1];把端点写成k或n会多反转一个元素或越界。- 辅助数组方案若只让形参指向新数组,并没有修改调用方持有的原数组,不符合原地修改要求。
相似题目
| 题目 | 难度 | 考察点 |
|---|---|---|
| 61. 旋转链表 | 中等 | 同样是环绕移位,但载体是链表,靠成环再断开而非反转 |
| 151. 反转字符串中的单词 | 中等 | 整体反转加逐词反转,是三次反转思想在变长块上的推广 |
| 186. 反转字符串中的单词 II | 中等 | 要求字符数组原地完成,反转法的空间约束比本题更硬 |
| 796. 旋转字符串 | 简单 | 只判断能否由轮转得到,用拼接后子串查找代替真正的移位 |
| 面试题 01.09. 字符串轮转 | 简单 | 与 796 同型,额外限制只能调用一次字符串包含判断 |
| 48. 旋转图像 | 中等 | 二维原地重排,用转置加行反转,是「多次反转合成目标置换」的变体 |
| 剑指 Offer 58 - II. 左旋转字符串 | 简单 | 左移方向,分界点变为 n - k,可直接验证反转法的对称性 |