1. 程式人生 > >leetcode:旋轉陣列

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%程式碼,起碼說明我這種思路是沒有錯誤的,雖然寫的很繁瑣,但依然是有成就感的,所以把它貼出來我不覺得羞恥,這是我的思考過程。