189. 轮转数组
题目链接
题目描述
给定一个整数数组 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]
解题思路
关键点
- 注意处理
k可能大于数组长度的情况,需要使用取模运算 - 需要原地修改数组,不能使用额外的数组(虽然方法1和方法2用了额外空间,但题目要求O(1)空间)
三种解法对比
方法一:双倍数组法
cpp
void rotate(vector<int>& nums, int k) {
int n = nums.size();
k = k % n; // 处理k大于n的情况
vector<int> nums1(nums); // 复制原数组
nums1.insert(nums1.end(), nums.begin(), nums.end()); // 拼接两份
nums.clear();
nums.insert(nums.begin(), nums1.begin() + n - k, nums1.begin() + 2 * n - k);
}
思路解析:
- 先把原数组复制一份,然后拼接到自己后面
- 从新数组的
n-k位置开始,取n个元素 - 用这个子数组替换原数组
示例:
nums = [1,2,3,4,5], k=2, n=5
nums1 = [1,2,3,4,5,1,2,3,4,5]
从位置 n-k=3 开始取5个:nums1[3]=4, [4]=5, [5]=1, [6]=2, [7]=3
结果:[4,5,1,2,3]
复杂度:
- 时间复杂度:O(n),需要遍历数组
- 空间复杂度:O(n),需要额外的数组空间
方法二:额外数组法
cpp
void rotate(vector<int>& nums, int k) {
int n = nums.size();
vector<int> nums1(n);
for(int i = 0; i < n; i++) {
nums1[(i + k) % n] = nums[i];
}
nums.assign(nums1.begin(), nums1.end());
}
思路解析:
- 创建一个和原数组同样大小的新数组
- 遍历原数组,将每个元素放到新数组的
(i+k)%n位置 - 用新数组替换原数组
示例:
nums = [1,2,3,4,5], k=2, n=5
i=0: nums1[(0+2)%5=2] = 1 → nums1[2]=1
i=1: nums1[(1+2)%5=3] = 2 → nums1[3]=2
i=2: nums1[(2+2)%5=4] = 3 → nums1[4]=3
i=3: nums1[(3+2)%5=0] = 4 → nums1[0]=4
i=4: nums1[(4+2)%5=1] = 5 → nums1[1]=5
nums1 = [4,5,1,2,3]
复杂度:
- 时间复杂度:O(n),需要遍历数组
- 空间复杂度:O(n),需要额外的数组空间
方法三:三次反转法(最优解)
cpp
void rotate(vector<int>& nums, int k) {
int n = nums.size();
k = k % n;
reverse(nums.begin(), nums.end()); // 反转整个数组
reverse(nums.begin(), nums.begin() + k); // 反转前k个
reverse(nums.begin() + k, nums.end()); // 反转剩下的
}
思路解析:
- 先反转整个数组
- 再反转前k个元素
- 最后反转剩下的n-k个元素
示例:
nums = [1,2,3,4,5], k=2, n=5
1. 整体反转: [5,4,3,2,1]
2. 反转前k=2个: [4,5,3,2,1]
3. 反转剩下的n-k=3个: [4,5,1,2,3]
数学原理:
向右旋转k位,相当于把后k个元素移到前面。通过三次反转可以巧妙地实现这个操作:
- 整体反转:把末尾元素放到开头
- 反转前k个:恢复被反转的尾部顺序
- 反转剩下的:恢复被反转的头部顺序
复杂度:
- 时间复杂度:O(n),三次反转操作
- 空间复杂度:O(1),原地修改,不需要额外空间
总结对比
| 方法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| 双倍数组法 | O(n) | O(n) | 思路直观,容易理解 | 需要额外O(n)空间 |
| 额外数组法 | O(n) | O(n) | 逻辑简单清晰 | 需要额外O(n)空间 |
| 三次反转法 | O(n) | O(1) | 最优解,原地修改 | 需要理解反转原理 |
注意事项
- 必须处理
k > n的情况,通过k = k % n取模 - 题目要求原地修改,但有些方法用了额外空间
- 三种方法都要掌握,但面试时尽量用第三种