问题
给定一个数组,将数组中的元素向右移动 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) 的原地算法。
其实我觉得叫旋转数组不如叫偏移数组。
解决
一开始没打算写的,后来写代码的过程中觉得这个题目没字面意思那么简单,写出来也是费了些功夫的,所以还是记录一下。
首先呐一开始并没有想用数组方法来解决,后来做的过程中觉得很绕,以为写完了,但有些用例还是不通过,这个我们后面讨论,所以就想到用数组方法多简单啊,所以就有了下面的代码:
var rotate2 = function(nums, k){
nums = nums.slice(nums.length-k%nums.length).concat(nums.slice(0,nums.length-k%nums.length))
}
先截取再拼接,写得多好,优雅。ok,放到线上,不通过,说结果不正确,把不正确的用例自己运行一遍完全没问题,这时就想到了一个问题,题目的要求是空间复杂度为O(1),然后再去MDN查slice()方法:
slice() 方法返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象。且原始数组不会被修改。
所以这种方式是行不通的。那只能再回归老的方式了,也就是我一开始研究出来的方法:
var rotate = function(nums, k) {
let currentpos = 0;
let tempmoved,tempwritten = 0;
//暂存即将被转移的元素
tempmoved = nums[(currentpos-k%nums.length+nums.length)%nums.length];
//循环数组长度次,每次循环先把当前要替换的元素存下来,把被转移的元素覆盖在被替换的元素的位置上,此时被覆盖的原元素也就成了下一次需要转移的数
//位置的计算:位置当然是偏移量加上当前位置,为了防止超出对数组长度取模
for(let j = 0;j <= nums.length-1;j++){
tempwritten = nums[currentpos];
nums[currentpos] = tempmoved;
currentpos =(k+currentpos)%nums.length;
tempmoved = tempwritten;
console.log("currentPos:",currentpos,(k+currentpos)%nums.length,(2*k+currentpos)%nums.length, nums)
}
};
主要是根据偏移量计算每个元素的最终是被谁替换了,利用变量暂存被替换的原元素,在下一次循环中再去覆盖被替换的,至于下一个被替换元素位置就是按照currentpos = (k+currentpos)%nums.length
的方法,因为每个元素都会偏移,所以我当然是循环数组长度次喽,但是后来就发现问题,数组长度取模偏移量如果没有余数,就会出现无论循环多少次都不会被替换的数,比如rotate([1,2,3,4],2),位置永远在0和2之间反复横跳。。。(代码中我特意把每次循环的位置都打印出来了)
currentPos: 2 0 2 [ 3, 2, 3, 4 ]
currentPos: 0 2 0 [ 3, 2, 1, 4 ]
currentPos: 2 0 2 [ 3, 2, 1, 4 ]
currentPos: 0 2 0 [ 3, 2, 3, 4 ]
如果陷入到循环中,那必然会再经过位置0,所以我就思考当位置再次经过0,我就把currentpos+1再开始是不是就没问题了呢?这时你会发现它又在1,3之间反复横跳了,其实想一下反复横跳的情况,因为最终会跳回的原位置,说明一次回到原位其实是替换了数字的1/k,所以,当我们完成了1/k遍历,currentPos+1,再来一遍,直到把nums.length次都做完。
var rotate = function(nums, k) {
let currentpos = 0;
let tempbefore,tempafter = 0;
tempbefore = nums[(currentpos-k%nums.length+nums.length)%nums.length];
if(nums.length%k==0){
for (let index = 0; index < k; index++) {
let currentpos = index;
let tempbefore,tempafter = 0;
tempbefore = nums[(currentpos-k%nums.length+nums.length)%nums.length];
for(let j = 0;j <= (nums.length/k)-1;j++){
tempafter = nums[currentpos];
nums[currentpos] = tempbefore;
currentpos =(k+currentpos)%nums.length;
tempbefore = tempafter;
// console.log(currentpos,nums,111)
}
}
}
else if (nums.length%(nums.length-k%nums.length)==0) {
for (let index = 0; index < (nums.length-k%nums.length); index++) {
let currentpos = index;
let tempbefore,tempafter = 0;
tempbefore = nums[(currentpos-k%nums.length+nums.length)%nums.length];
for(let j = 0;j <= nums.length/(nums.length-k%nums.length)-1;j++){
tempafter = nums[currentpos];
nums[currentpos] = tempbefore;
currentpos =(k+currentpos)%nums.length;
tempbefore = tempafter;
// console.log(currentpos,nums,111)
}
}
}
else{
for(let j = 0;j <= nums.length-1;j++){
tempafter = nums[currentpos];
nums[currentpos] = tempbefore;
currentpos =(k+currentpos)%nums.length;
tempbefore = tempafter;
}
}
// console.log(nums)
};
我知道相对那些标准答案,我已经写的是非常累赘了,但是最后提交测试时执行速度是打败了97%
代码,起码说明我这种思路是没有错误的,虽然写的很繁琐,但依然是有成就感的,所以把它贴出来我不觉得羞耻,这是我的思考过程。