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