1. 程式人生 > 其它 >LeetCode—雙指標第189題:旋轉陣列

LeetCode—雙指標第189題:旋轉陣列

189:旋轉陣列(難度:中等)

本題要求三種寫法

給定一個數組,將陣列中的元素向右移動k個位置,其中k是非負數。

進階:

儘可能想出更多的解決方案,至少有三種不同的方法可以解決這個問題。
你可以使用空間複雜度為O(1) 的原地演算法解決這個問題嗎?


示例 1:

輸入: nums = [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:

輸入:nums = [-1,-100,3,99], k = 2
輸出:[
3,99,-1,-100] 解釋: 向右旋轉 1 步: [99,-1,-100,3] 向右旋轉 2 步: [3,99,-1,-100] 提示: 1 <= nums.length <= 2 * 104 -231 <= nums[i] <= 231 - 1 0 <= k <= 105

寫法1:使用額外的陣列

即另外開闢一個新陣列,按照規則把源陣列的元素按照移動後的位置放到新陣列中

程式碼:

class Solution {
        public void rotate(int[] nums, int k) {
            int n = nums.length;
            
int[] newArr = new int[nums.length]; for(int i = 0;i<nums.length;i++){ newArr[(i + k) % n] = nums[i]; } System.arraycopy(newArr, 0, nums, 0, nums.length); } }

寫法2:環裝替代(難)

現有[1,2,3,4,5]這個陣列,k = 3,定義一個temp來臨時儲存元素,這樣就不需要開闢一個新陣列

1 2 3 1 5 temp = 4

1 4 3 1 5 temp = 2

1 4 3 1 2 temp = 5

1 4 5 1 2 temp = 3

3 4 5 1 2 完成,共交換了5次,即交換次數 = 陣列長度

但是若有[1,2,3,4]這個陣列,k = 2,使用此方法:

1 2 1 4 temp = 3

3 2 1 4 temp = 1

3 2 1 4 temp = 1

1 2 1 4 temp = 3

1 2 3 4 temp = 1

會發現一直在重複對調1和3,陷入死迴圈,那麼這個時候就需要在陷入迴圈的時候讓“指標”向前移動一位,來完成剩下的部分

我們發現,是否陷入迴圈是由陣列元素個數和k的最大公因數決定的,第一個例子5和3的最大公因數是1,第二個例子4和2的最

大公因數是2,即最大公因數是奇數那麼不會陷入迴圈,反之則會陷入迴圈,那麼我們可以引入一個count = gcd(nums.length,k),

具體請看程式碼:

class Solution {
    public void rotate(int[] nums, int k) {
        int n = nums.length;
        k = k % n;
        int count = gcd(k, n);
        for (int start = 0; start < count; ++start) {
            int current = start;
            int prev = nums[start];
            do {
                int next = (current + k) % n;
                int temp = nums[next];
                nums[next] = prev;
                prev = temp;
                current = next;
            } while (start != current);
/*若最大公因數為奇數,那麼當start == current時,這時已經完成了一次全部交換,之後退出迴圈,start++,因為進行了count次整體交換,又因為count是奇數,
所以最後還會得到正確陣列:
如:1 2 3 4 5 6 k = 3 ,count = 3
  ↑ 第一次交換
  4 5 6 1 2 3 start = 1所以這次迴圈從nums[1]開始
↑ 第二次交換
1 2 3 4 5 6 start = 2 所以這次迴圈從nums[2]開始
↑ 第三次交換
4 5 6 1 2 3 所以最後仍是正確答案
若最大公因數為偶數,那麼當start == current時,這時已經完成了一次全部交換,之後退出迴圈,start++,因為進行了count次整體交換,又因為count是偶
數,++start就會起到讓“指標”向前移動一位的作用。
所以最後還會得到正確陣列:
如:1 2 3 4 k = 2,count = 2
  ↑ 第一次交換
  3 2 1 4
start = 1所以這次迴圈從nums[1]開始
   ↑ 第二次交換
  3 4 1 2 所以最後仍是正確答案

*/

}
    }

    public int gcd(int x, int y) {
        return y > 0 ? gcd(y, x % y) : x;
    }
}

寫法3:陣列翻轉

這種做法就是將一個數組翻轉,然後從第k位分開,nums[0]~nums[k mod n - 1 ]的數進行翻轉,nums[k mod n]到nums[n - 1]進行翻轉,然後將兩個陣列翻轉連線在一起

如[1,2,3,4,5] , k = 2

5 4 3 2 1

分開:

5 4 | 3 2 1

翻轉:

4 5|1 2 3 得到正確陣列

class Solution {
        public void rotate(int[] nums, int k) {
            k = k % nums.length;
            reverse(nums, 0, nums.length - 1);
            reverse(nums, 0, k - 1);
            reverse(nums, k, nums.length - 1);
        }

        public void reverse(int[] nums, int start, int end) {
            while (start < end) {
                int temp = nums[end];
                nums[end] = nums[start];
                nums[start] = temp;
                ++start;
                --end;
            }
        }
}

------------恢復內容結束------------