题目
给定一个整数数组 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]
示例 2:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]
提示:
1 <= nums.length <= 10的五次方
-2的31次方 <= nums[i] <= 2的31次方 - 1
0 <= k <= 10的五次方
进阶:
尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?
详细解析
中译中
题目很清晰
思路讲解
- 首先看到题目容易想到的是遍历k次循环,然后每次循环往后挪一次
- 该算法效率极其低下,建议不要这么想
- 可以对此改进一下,记录后面k个数,然后一次循环把原数组里的数挪到指定位置
- 再把后面k个数挪到指定为止
- 这个要注意,当k比原数组的长度要长怎么办
- 显然是对原数组进行取模处理
- 现在我们要改进算法
- 空间效率达到o(1)
- 容易想到在第n-k个元素的时候,一定会出现在数组首位,然后,n-k后面的直到n,依次排列
- 所以可以分为两部分,一部分是k个元素及以后的,一部分为首个元素到k的,两个最终结果都是正序
- 同时不能开辟新空间,所以,我们可以把元素进行翻转,前后调换,但由于对正个调换会使两部分都变成倒序,所以,还需要对两部分分别再调换一次(如图)
代码
改进前
class Solution {
public void rotate(int[] nums, int k) {
int n = nums.length;
int[] arr = new int[k];
k %= n;
for (int i = k - 1; i >= 0; i--) {//把后面的元素放在一个数组里
arr[i] = nums[n - (k - i)];
}
for (int i = n - 1; i >= k; i--) {//开始挪位置
nums[i] = nums[i - k];
}
for (int i = 0; i < k; i++) {//赋值
nums[i] = arr[i];
}
}
}
改进后
class Solution {
public void rotate(int[] nums, int k) {
int n = nums.length;
k%=n;
reverse(nums, 0, n-1);
reverse(nums, 0, k-1);
reverse(nums, k, n-1);
}
public void reverse(int[] nums,int start,int end){
while(start<end){
int temp = nums[end];
nums[end] = nums[start];
nums[start]=temp;
end--;
start++;
}
}
}