1. 题目
题目描述
给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
要求尽可能想出多种实现方案,至少满足空间 O (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
输入:nums = -1,-100,3,99, k = 2
输出:3,99,-1,-100
约束
-
(1 <= nums.length <= 10^5)
-
(-2^{31} <= numsi <= 2^{31}-1)
-
(0 <= k <= 10^9)
2. 最佳解题思路描述(三次反转法,原地最优)
核心原理
数组右移 k 位等价于:把后 k 个元素挪到数组前面。利用反转分段实现,三步操作:
-
对整个数组全部反转;
-
反转前 k 个元素;
-
反转剩下 n-k 个元素。
前置关键:
k %= n 取模压缩,消除 k ≥ 数组长度的无效旋转(旋转 n 次等于原数组不变)。
举例 nums=1,2,3,4,5,6,7,k=3,n=7
-
整体反转:7,6,5,4,3,2,1
-
反转前 3 个:5,6,7,4,3,2,1
-
反转后 4 个:5,6,7,1,2,3,4
优势
-
时间复杂度 (O(n)),每个元素仅被反转 2 次;
-
空间复杂度 (O(1)),完全原地修改,不新开数组;
-
代码极简,标准面试最优解。
3. 我的可优化代码(无逻辑错误,标准最优,仅补充说明细节)
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int n = nums.size();
k %= n;
reverse(nums.begin(), nums.end());
reverse(nums.begin(), nums.begin() + k);
reverse(nums.begin() + k, nums.end());
}
};
代码说明
-
正确性:逻辑无任何 bug,可通过全部测试用例,是官方推荐最优写法;
-
关键点不能省略:
-
k %= n:k 极大时避免无效循环,例如 n=3,k=1000,实际只需右移 1 次; -
三次 reverse 顺序固定,调换顺序结果错误;
-
-
小拓展:如果题目要求向左轮转 k 位,可调整反转分段逻辑。
4. 标准规范完整代码(带上头文件说明)
#include <algorithm>
#include <vector>
using namespace std;
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int n = nums.size();
k = k % n;
// 整体反转
reverse(nums.begin(), nums.end());
// 反转前k段
reverse(nums.begin(), nums.begin() + k);
// 反转剩余后段
reverse(nums.begin() + k, nums.end());
}
};
5. 总结
-
三次反转是本题空间 O (1) 最优模板,代码简洁、效率高,面试首选;
-
固定三步口诀:全反转 → 前 k 反转 → 后段反转;
-
必须先对 k 取模,否则大数 k 无意义;
-
原地类旋转数组优先考虑分段反转思想。
6. 相关知识拓展
拓展 1:额外数组辅助法(空间 O (n),思路简单但不推荐面试)
新建数组存储后 k 位 + 前 n-k 位,再拷贝回原数组:
void rotate(vector<int>& nums, int k) {
int n = nums.size();
k %= n;
vector<int> tmp;
for(int i = n - k; i < n; i++) tmp.push_back(nums[i]);
for(int i = 0; i < n - k; i++) tmp.push_back(nums[i]);
nums.swap(tmp);
}
缺点:占用额外数组空间,不符合原地要求。
拓展 2:循环交换法(原地 O (1),逻辑复杂)
从下标 0 开始,每次把元素放到目标旋转位置,记录替换次数,直到全部元素归位,无需反转,但需要额外变量记录中间值,代码可读性差。
拓展 3:反转模板通用场景
数组分段交换、字符串旋转、字符串左移 / 右移均可使用分段反转思想;
例如字符串左旋:abcdefg 左旋 2 位得到 cdefgab,同样用三段反转实现。
拓展 4:复杂度对比
-
三次反转原地法:时间 (O(n)),空间 (O(1))(最优);
-
辅助数组拷贝:时间 (O(n)),空间 (O(n));
-
循环逐个移动暴力法:时间 (O(nk)),大数据超时,完全不推荐。