首先祝大家
新的一年旺旺旺!!!

核心思路
向右轮转 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)
思路:通过三次反转实现原地轮转,是满足进阶要求的最优解法:
- 反转整个数组
- 反转前
k个元素 - 反转后
n-k个元素
- 时间复杂度:O (n)
- 空间复杂度:O (1)
原理示例 :原数组:[1,2,3,4,5,6,7],k=3
-
反转整个数组 →
[7,6,5,4,3,2,1] -
反转前 3 个 →
[5,6,7,4,3,2,1] -
反转后 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;
}
}
来源:力扣(LeetCode) 著作权归作者所有。

