leetcode:旋转数组

问题

给定一个数组,将数组中的元素向右移动 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%代码,起码说明我这种思路是没有错误的,虽然写的很繁琐,但依然是有成就感的,所以把它贴出来我不觉得羞耻,这是我的思考过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值