LeetCode189 Rotate Array
最近重新开始刷题了! 因为之前写过的错题总是一错再错,想找个地方总结一下错题,方便之后回忆思路!
题目
Link: https://leetcode.com/problems/rotate-array/
Given an array, rotate the array to the right by k steps, where k is non-negative.
Example1:
Input: nums = [1,2,3,4,5,6,7], 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]
Example2:
Input: nums = [-1,-100,3,99], 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]
思路
一开始想的是把 nums 分成 k 组 ( i % len = 0, i % len = 1 … i % len = k-1, len = nums.length), 然后按组更新nums。 后来发现因为这是一个rotate array, 每移动 k steps 不一定只会走到 i % len 相同的地方。
比如说,nums = [1,2,3,4,5,6,7],k = 3。
按最开始想的分组应该是:
i % len = 0 : 1 -> 4 -> 7
i % len = 1 : -> 2 -> 5
i % len = 2 : -> 3 -> 6
但实际模拟一下移动 k steps 的情景,发现是:
1 -> 4 -> 7 -> 3 -> 6 -> 2 -> 5
觉得大致思路应该没什么问题,但是卡在了怎么排除已经改变过的那些数组元素。 试了好多种方法都有bug, 直到搜到一篇博文:
https://blog.csdn.net/qq_28584889/article/details/83655019
才发现用一个count来统计已经改变过的元素数量,跟数组长度比较,来决定是否跳出循环就可以了!
随后我又想到了一个问题:我是按 i % len 的分组从小到大循环的,当count < nums.length 的时候,要怎么知道我接下来按顺序的这个分组是没有被改变过的呢?
有点不知道怎么解释,举个例子吧:现在有k个分组( 0 到 k-1),从0组第一个元素开始改元素。 如果0组的最后一个元素改完之后是跳到1组第一个元素, 那么1组最后一个元素改完之后一定会跳到2组第一个元素; 如果0组最后一个元素改完之后跳到2组第一个元素, 那么2组最后一个元素改完之后一定会跳到4组第一个元素,当跳回0组第一个元素之后, 我们就该换下一组。 下一组就是1组->3组->5组这个顺序。所以,接下来按顺序的这个分组一定是没有被改变过的。
上代码
public void rotate(int[] nums, int k) {
if(nums == null || nums.length < 2 || k == 0|| k== nums.length) {
return;
}
k = k % nums.length;
int count = 0;
for(int i = 0; i < k; i++) {
//可以按顺序 因为如果 i+1被命中了那i+2一定会在同一轮被命中
if(count == nums.length) {
break;
}
int next = (i + k) % nums.length;
int prev = nums[i]; //需要一个变量来保存放在next位置的元素 因为每次被替换掉的nums[next]都是下一个next需要存放的元素
while(next != i) {
int temp = nums[next];
nums[next] = prev;
prev = temp;
next = (next + k) % nums.length;
count ++;
}
nums[next] = prev;
count++;
}
}
时间复杂度 O(n), 空间复杂度 O(1)
Using Reverse
在LeetCode Solution里看到了用reverse的做法也挺妙的。 感觉做题的时候还是应该多观察规律呀!
大概记录一下:
nums = [1, 2, 3, 4, 5, 6, 7], k = 3
改变后的最终结果应该是:
nums = [5, 6, 7, 1, 2, 3, 4]
有点像把nums分成两组,直接后移的组(1, 2, 3, 4) 和需要rotate到开头的组(5, 6, 7)。把最后k个元素放到index = 0开始的地方, 把前(len - k)个元素往后移k个位置。
所以可以先把整个array翻转:
7, 6, 5, 4, 3, 2, 1
这步约等于把两个组放在应该放的地方,只不过组内元素顺序不对
然后分别把两个组的元素翻转,也就是把组内元素顺序复原
5, 6 ,7 || 1, 2, 3, 4
时间复杂度O(n), 空间复杂度O(1)