给定一个整数数组 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 <= 105
-231 <= nums[i] <= 231 - 1
0 <= k <= 105
进阶:
- 尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。
- 你可以使用空间复杂度为
O(1)
的 原地 算法解决这个问题吗?
错误思路解法:
class Solution {
public void rotate(int[] nums, int k) {
//将要换位置的元素下标
int index = 0;
//上一个位置的值
int pre = nums[0];
//互换变量时的临时变量
int temp = 0;
//循环k次
for (int i = 0; i < nums.length; i++) {
//计算上个位置移到下个位置的下标, 如果超过数组长度区余数.相当于从尾部继续走到头部
index = (index+k)%nums.length;
temp = pre;
//上一个位置的值更新为当前index位置的值
pre = nums[index];
//当前index位置的值更新为上一次位置的值
nums[index] = temp;
}
}
}
执行第一个样例时通过的,第二个就失败了 原因是下一个位置的下一轮位置刚好是自己循环了.其他元素没有遍历到
![189.png](https://i-blog.csdnimg.cn/blog_migrate/d6b45553a6d8593897c8d1c2c946386d.png)
解法一:
这种解法虽然空间复杂度正常,但是时间复杂度是k*n. 大组数时运行超时
class Solution {
public void rotate(int[] nums, int k) {
//每次移动一位,遍历k次
for(int x=0; x<k; x++){
int pre = nums[0];
int temp = 0;
//一次将数组中位置向前移动一位
for(int i=0; i<nums.length; i++){
temp = nums[i];
nums[i] = pre;
pre = temp;
//到最后一位时,把末位移动到数组头
if(i == nums.length-1){
nums[0] = pre;
}
}
}
}
}
解法2:只遍历一次,借助O(n)的额外空间
class Solution {
public void rotate(int[] nums, int k) {
int[] temp = new int[nums.length];
//复制一个数组,取数从这里取
for (int i = 0; i < nums.length; i++) {
temp[i] = nums[i];
}
int next=0;
for (int i = 0; i < nums.length; i++) {
//取余数,i+k可能大于数组长度
next = (i+k)%nums.length;
nums[next] = temp[i];
}
}
}
官方提供的时间O(n)空间O(1)的题解:
数组翻转
该方法基于如下的事实:当我们将数组的元素向右移动 kkk 次后,尾部 k mod nk\bmod nkmodn 个元素会移动至数组头部,其余元素向后移动 k mod nk\bmod nkmodn 个位置。
该方法为数组的翻转:我们可以先将所有元素翻转,这样尾部的 k mod nk\bmod nkmodn 个元素就被移至数组头部,然后我们再翻转 [0,k mod n−1][0, k\bmod n-1][0,kmodn−1] 区间的元素和 [k mod n,n−1][k\bmod n, n-1][kmodn,n−1] 区间的元素即能得到最后的答案。
我们以 n=7n=7n=7,k=3k=3k=3 为例进行如下展示:
操作 | 结果 |
---|---|
原始数组 | 1 2 3 4 5 6 7 |
翻转所有元素 | 7 6 5 4 3 2 1 |
翻转 [0,k mod n−1] 区间的元素 | 5 6 7 4 3 2 1 |
翻转 [k mod n,n−1] 区间的元素 | 5 6 7 1 2 3 4 |
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 += 1;
end -= 1;
}
}
}
复杂度分析
时间复杂度:O(n)O(n)O(n),其中 nnn 为数组的长度。每个元素被翻转两次,一共 nnn 个元素,因此总时间复杂度为 O(2n)=O(n)O(2n)=O(n)O(2n)=O(n)。
空间复杂度:O(1)O(1)O(1)。
作者:力扣官方题解
链接:https://leetcode.cn/problems/rotate-array/solutions/551039/xuan-zhuan-shu-zu-by-leetcode-solution-nipk/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
贪心法旋转,和方法二一样,但是不用求最大公约数,例如
- 从
i=0
开始, - 让i位置的值赋值给
(i+k)%n
的位置 - 然后再让
(i+k)%n
的位置之前的数赋值给((i+k)%n+k)%n
的位置 - …,,
- 这样一定会循环到
0
,如果循环到0
发现循环次数小于n
,就直接让i=1
开始 - 然后重复上面
i=0
的操作,一定会循环回到1
- 如果循环的次数还是小于
n
,在让i=2
继续,直到循环次数等于n
虽然能够通过,但是并没有严格的证明
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n = len(nums)
k %= n
count = i = start = 0
temp = nums[0]
while True:
idx = (i + k) % n
temp, nums[idx] = nums[idx], temp
i = idx
count += 1
if count>= n:
break
if i == start:
start += 1
i = start
temp = nums[start]
作者:力扣题解
链接:https://leetcode.cn/problems/rotate-array/solutions/551039/xuan-zhuan-shu-zu-by-leetcode-solution-nipk/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。