Rotate an array of n elements to the right by k steps.
For example, with n = 7 and k = 3, the array [1,2,3,4,5,6,7]
is rotated to [5,6,7,1,2,3,4]
.
Note:
Try to come up as many solutions as you can, there are at least 3 different ways to solve this problem.
Related problem: Reverse Words in a String II
基本思路一:
设立一个后备容器,将最后的k个元素移入后备容器中。
然后再将原数组吐剩余元素整体向移动k个位置。
最后将后备容器中保存的元素,覆盖到原数组的头部。
class Solution {
public:
void rotate(vector<int>& nums, int k) {
const int n = nums.size();
k %= n;
vector<int> backup(nums.end()-k, nums.end());
for (int i=n-1; i>=k; i--)
nums[i] = nums[i-k];
for (int i=0; i<k; i++)
nums[i] = backup[i];
}
};
此算法的空间复杂度为O(n),不满足题目要求。
基本思路二:
数组末尾的k个元素,在移动完成后,将位于数组头部。
将数组头部k个元素与尾部k个元素,进行交换。
交换后,数组前k个位置已经处于最终结果。 只需要于处理数组余下部分。
忽略此头部的k个元素,将得到一个新数组。
而新数组中,继续将数组头k个元素与尾部k个元素进行交换。
则新数组的前k个元素处于最终结果。只需要处理数组余下部分。
如此重复下去。
概括起来,就是将数组末尾k个部分,作为中转。
要注意的事,收敛条件: 当数组剩余元素的个数,小于k时。 此时需要取余。
循环直到,余数为0,即不需要移动。
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int n = nums.size();
k %= n;
auto iter = nums.begin();
while (k) {
for (int i=0; i<k; i++) {
swap(*iter++, *(nums.end()-k+i));
}
n -= k;
k %= n;
}
}
};
基本思路三:
数组向后移动k步。只需要把数组尾部k个元素,移动数组头部。而其余元素顺序向后移动k个位置。
先进行数组整体反转。 这样,原数组后部的k个元素将位于数组前面k个位置。 但顺序是刚相反的。
对数组前k个位置进行反转。 将达到的期望顺序。 至此,数组最后的k个元素移动完成。
而此时,剩余的n-k个位置,也达到预定位置。只是与最终结果不同的是,顺序刚好相反。
class Solution {
public:
void rotate(vector<int>& nums, int k) {
k %= nums.size();
reverse(nums.begin(), nums.end());
reverse(nums.begin(), nums.begin()+k);
reverse(nums.begin()+k, nums.end());
}
};