问题:
思路:
方法1:
最简单的,声明一个新列表,然后遍历记录
方法2:
引入一个标记数,用来更改标记的数值,然后一个个找已经存入新的地点的值
循环k步,即可得到结果
1 | 2 | 3 | 4 | 5 | 6 | flag | |
5 | 6 | ||||||
4 | |||||||
3 | |||||||
2 | |||||||
1 | |||||||
flag | |||||||
复杂度分析:
时间复杂度:O(n) //n为移动多少位
空间复杂度:O(1)
public void rotate(int[] nums, int k) {
k = k%nums.length;
for(int i=1;i<=k;i++){
int flag = nums[nums.length-1];
for(int j = nums.length-1;j>=1;j--){
nums[j]=nums[j-1];
}
nums[0] = flag;
}
}
当然,这样超时了……不过,是为了给下一个方法做铺垫,所以我保留了下来
方法3:
方法2的延伸,最开始我试图寻找,能不能在2的基础上实现一步搞定,然后发现那样引入的标记位置,可能要 数组后边的都需要了
然后在想,可不可以跟随我已经存好的,去挨个找之前的;
来画个表,假设需要移动2步
1 | 2 | 3 | 4 | 5 | 6 | flag | |
step1 | 4 | 6 | |||||
step2 | 2 | ||||||
step3 | 6 | ||||||
step4 | 5 | ||||||
step5 | 3 | ||||||
step6 | 2 |
如上表,在走到第三步的时候发生了循环,
循环的时候怎么处理呢,可以在出现循环的数字,也就是填完2之后,往前进一位,进行下一轮;
不难看出,6步,分2组搞定
所以可以尝试,步骤如下:
1、记录第一个位置的值
2、替换第一个位置上的数,然后顺着这条链,往前探索,直到再一次出现要填第一个位置时
3、一组结束后,判断是否已经替换了n个数了,如果没有,往前走一位,然后再次重复1 2步骤
注意,我这里时倒着往前走的,所以时减法,但是又不能让数变成负的,所以每个更新完找到的下一个数的公式是:
i = (i + nums.length - k) % nums.length
如果选择往前走,那就是另外一种分析方法了,那样的公式,也会比这个简单
但是不管是向前还是向后,这几个公式都很重要。
代码:
public void rotate(int[] nums, int k) {
k = k % nums.length;
if (k != 0) {
int sum = 0;// 用来记录已经更新了多少个数
int index = -1;// 用来记录每轮的第一个;
while (sum < nums.length) {
index++;
int flag = nums[index];
nums[index] = nums[(index + nums.length - k) % nums.length];
sum++;
for (int i = (index + nums.length - k) % nums.length; i != index
&& sum < nums.length; i = (i + nums.length - k) % nums.length) {
if ((i + nums.length - k) % nums.length == index) {
nums[i] = flag;
sum++;
} else {
nums[i] = nums[(i + nums.length - k) % nums.length];
sum++;
}
}
}
}
}
我的代码 还没有进一步优化,优点费时间,但是对于java来讲,空间还好
细分析会发现,其实,数组的长度,走多少步,他们的最大公因数,就是可以分的组数,也是后来惊喜的发现是一个经典的方法
所以,可以提前算出 最大公倍数,然后就避免了后期的一步一步判断是不是已经更新完了,代码我还没写~
这是我尝试了几个之后才感觉到的规律,尝试的心路历程如下,当然也可以不看啦~
数组 | 1 | 2 | 3 | 4 | 5 | 6 | |
1组 | 6 | 2 | 4 | ||||
2组 | 5 | 1 | 3 |
惊喜的发现:6个数移动2步的时候,可以分成两组
再多尝试几种情况:
数组 | 1 | 2 | 3 | 4 | 5 | 6 | |
1组 | 6 | 3 | |||||
2组 | 5 | 2 | |||||
3组 | 4 | 1 |
6个数移动3步,可以分成3组,试想,难道跟移动的步数有关系?
数组 | 1 | 2 | 3 | 4 | 5 | 6 | |
1组 | 4 | 6 | 2 | ||||
2组 | 3 | 5 | 1 |
6个数移动4步的时候,就不是4组,而是2组,这个时候就可以大致才出来,可能与公倍数有关系
不妨再7个数试试,会发现不管移动多少步,都只能分成1组了
数组 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
1组 | 5 | 6 | 7 | 1 | 2 | 3 | 4 |
方法4:
看了答案后,发现了很牛的反转
例如 1 2 3 4 5 6 7 //移动3步
反转 7 6 5 4 3 2 1 //先对整个数组反转,这样就可以把,循环的时候后边的数字移到前边
再反 5 6 7 1 2 3 4 //在分别对 前3个反转,还有后边剩余的部分
代码:
public void rotate(int[] nums, int k) {
k = k%nums.length;
if(k!=0){
reverse(nums,0,nums.length-1);
reverse(nums,0,k-1);
reverse(nums,k,nums.length-1);
}
}
public int[] reverse(int[] nums, int a , int b){
//双指针实现反转
while(a<b){
int m = nums[a];
nums[a] = nums[b];
nums[b] = m;
a++;
b--;
}
return nums;
}