原题:
Given an array, rotate the array to the right by k steps, where k is non-negative.
Example 1:
Input:[1,2,3,4,5,6,7]
and k = 3 Output:[5,6,7,1,2,3,4]
Explanation: rotate 1 steps to the right:[7,1,2,3,4,5,6]
rotate 2 steps to the right:[6,7,1,2,3,4,5]
rotate 3 steps to the right:[5,6,7,1,2,3,4]
Example 2:
Input:[-1,-100,3,99]
and k = 2 Output: [3,99,-1,-100] Explanation: rotate 1 steps to the right: [99,-1,-100,3] rotate 2 steps to the right: [3,99,-1,-100]Note:
- Try to come up as many solutions as you can, there are at least 3 different ways to solve this problem.
- Could you do it in-place with O(1) extra space?
就是数组位移,先是暴力解法(微微减少了暴力程度),结果超时,代码:
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int i=0;
int k2=k%nums.size();
while(i<k2){
for(int j=0;j<nums.size();j++){
swap(nums[0],nums[j]);
}
i++;
}
}
};
然后创建额外一个vector可以节约大量时间,不过会浪费很多空间,结果:
Success
Runtime: 12 ms, faster than 99.91% of C++ online submissions for Rotate Array.
Memory Usage: 9.6 MB, less than 42.22% of C++ online submissions for Rotate Array.
代码:
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int i=0;
int s=nums.size();
vector<int> temp(s);
for(int i=0;i<s;i++){
temp[(i+k)%s]=nums[i];
}
nums.swap(temp);
}
};
题中要求额外空间O(1),那这几个方法肯定都不行,那只能找规律了,经过观察一开始发现如果nums的size和k%size互质的话只要一直交换n和n+k的元素就行,比如[1,2,3,4,5,6,7],k=3时1->4->7->3->6->2->5->1正好完整一遍,但是如果有公因数的话就会死循环,比如[1,2,3,4,5,6],k=2时1->3->5->1就不会访问别的变量。然后一想往右移一位再来一遍就行了,而且经过观察右移的次数跟最大公约数的大小有关,gcd1(互质)不需位移即只需循环一次,gcd2位移一次即循环两次以此类推,这样就很容易写出代码,结果:
Success
Runtime: 12 ms, faster than 99.88% of C++ online submissions for Rotate Array.
Memory Usage: 9.5 MB, less than 72.10% of C++ online submissions for Rotate Array.
代码:
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int size=nums.size();
int stride=k%size;
int s=__gcd(size,k);
for(int i=0;i<s;i++){
int j=i;
while((j+stride)%size!=i){
swap(nums[i],nums[(j+stride)%size]);
j=(j+stride)%size;
}
}
}
};
然后发现这个解法和solution3相似,不过居然有更简洁的solution4,只需单纯reverse全部数组再分别reverse前k个和后n-k个就行,原文是这样的:
Approach #4 Using Reverse [Accepted]
Algorithm
This approach is based on the fact that when we rotate the array k times, kk elements from the back end of the array come to the front and the rest of the elements from the front shift backwards.
In this approach, we firstly reverse all the elements of the array. Then, reversing the first k elements followed by reversing the rest n−kn−kelements gives us the required result.
Let n=7n=7 and k=3k=3.
Original List : 1 2 3 4 5 6 7 After reversing all numbers : 7 6 5 4 3 2 1 After reversing first k numbers : 5 6 7 4 3 2 1 After revering last n-k numbers : 5 6 7 1 2 3 4 --> Result
java
public class Solution { public void rotate(int[] nums, int k) { k %= nums.length; reverse(nums, 0, nums.length - 1); reverse(nums, 0, k - 1); reverse(nums, k, nums.length - 1); } public void reverse(int[] nums, int start, int end) { while (start < end) { int temp = nums[start]; nums[start] = nums[end]; nums[end] = temp; start++; end--; } } }
Complexity Analysis
Time complexity : O(n)O(n). nn elements are reversed a total of three times.
Space complexity : O(1)O(1). No extra space is used.
代码很简单,不过要注意c++的reverse函数并不包含最后一个函数,比如reverse(a.begin(),a.begin()+5)并不包含a[5]这个元素,结果:
Success
Runtime: 12 ms, faster than 99.88% of C++ online submissions for Rotate Array.
Memory Usage: 9.5 MB, less than 70.92% of C++ online submissions for Rotate Array.
代码:
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int size=nums.size();
int k2=k%size;
reverse(nums.begin(),nums.end());
reverse(nums.begin(),nums.begin()+k2);
reverse(nums.begin()+k2,nums.end());
}
};