20.4.10 旋转数组 简单
我没懂为什么两个指标都这么差,看代码的时间复杂度是O(n),空间复杂度是O(1)。
题目
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: [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:
输入: [-1,-100,3,99] 和 k = 2
输出: [3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
说明:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
要求使用空间复杂度为 O(1) 的 原地 算法。
解题思路
- 暴力解法应该都会,时间复杂度O(kn),两层循环,第一层套次数,第二层重复移动数组,每次移一位;
- 想着取巧,莫名其妙就实现成了环形交换;
- 实现思路就是,从第一个数开始,把它放在它的终点位置,那么此时取代的数就消失了,我们要提前先把它存起来;
- 继续找存起来的数的终点位置,重复3的操作,总共n次,因为只有n个数;
- 当然存在特殊情况,比如nums有6个数,k为2的时候,1换3,3换5,5又会换到1,那么这时再继续的话,2、4就操作不到了。所以当5换到1后,1就要变为下一个数,即2,继续重复操作即可。
代码思路
- 先除去特殊情况,k%nums.size()==0的情况就是,比如有6个元素的数组移12位或24位,其实都是不用移动的;
- 循环走n遍,index是当前遍历的索引,flag为未重复的起点索引(用于解决解题思路5中的情况),save1是保存被取代位置的元素,save2是保存要去取代别人的元素(反正我刚开始用一个save就是不行,用两个就解决了);
- 每次循环开始,save1就存起来被取代的元素,然后更新这个位置的值,即等于save2,再更新索引index为save1要取代的位置;
- 若更新index后index等于起点索引flag,则要更新index和起点索引index,都+1即可,同时save1不需要再保存这个被取代的元素了,因为这个元素已经移动过了,直接更新为下一次的起点位置,赋值给save2。
代码
class Solution {
public:
void rotate(vector<int>& nums, int k) {
if(nums.size() == 0 || k == 0 || k%nums.size() == 0) return;
for(int i = 1, index = 0, flag = 0, save1, save2 = nums[0]; i <= nums.size(); i++){
save1 = nums[(index + k) % nums.size()];
nums[(index + k) % nums.size()] = save2;
index = (index + k) % nums.size();
if(index == flag){
save1 = nums[++index];
flag = index;
}
save2 = save1;
}
}
};