【數論】C039_旋轉數組(枚舉 | 求餘作差法 | 三次翻轉)

一、題目描述

Given an array, rotate the array to the right by k steps, where k is non-negative.

Input: [1,2,3,4,5,6,7] and k = 3
Output: [5,6,7,1,2,3,4]
Explanation:
rotate 1 steps to the right: [7,1,2,3,4,5,6]
rotate 2 steps to the right: [6,7,1,2,3,4,5]
rotate 3 steps to the right: [5,6,7,1,2,3,4]

二、題解

方法一:枚舉

算法

  • 對於每一次旋轉 kik_i,首尾位置我們採取首尾交換;
  • 非首位區域採取前覆蓋後。

* 超時失誤:因爲沒有用戶 k 對 N 取餘,導致超時,例如 N=3k=6N = 3,k = 6 時,旋轉 6%3=06\%3=0 次和旋轉 66 次的結果是一樣的。

public void rotate(int[] nums, int k) {
    int N = nums.length;
    k %= N;
    for (int i = 0; i < k; i++) {
        int last = nums[N-1];
        for (int j = N-1; j >= 1; j--) {
            nums[j] = nums[j-1];
        }
        nums[0] = last;
    }
}

複雜度分析

  • 時間複雜度:O(k×N)O(k × N)
  • 空間複雜度:O(1)O(1)

方法二:求餘取差法(新數組)

其實計算機並沒有啥循環 xx,都是通過求餘 % 來達到回到起點的效果。

  • 新開闢一個數組 arr,用來記錄每一輪的位置的值。
  • 原數組下標 i 與新數組 arr 的下標對應關係是:i=(i+k)%Ni = (i + k) \% N
public void rotate(int[] nums, int k) {
    int N = nums.length;
    k %= N;
    int[] arr = new int[N];

    for (int i = 0; i < N; i++) {
        arr[(i+k)%N] = nums[i];
    }
    for (int i = 0; i < N; i++)
        nums[i] = arr[i];
}   

複雜度分析

  • 時間複雜度:O(n)O(n)
  • 空間複雜度:O(n)O(n)

方法三:三次翻轉

如果你足夠細心,我們會發現樣例中的 k%N 個元素被移動到了前面,而 N - (k%N) 個元素被移動到了上述 k%N 個元素的後面。知道本質,我們可以這樣寫算法:

  • 先將 N 個元素全部翻轉。
  • 然後翻轉翻轉前 k%N 個元素。
  • 最後翻轉後 N - (k%N) 個。
public void rotate(int[] nums, int k) {
    int N = nums.length;
    k %= N;
    reverse(nums, 0, N-1);
    reverse(nums, 0, k-1);
    reverse(nums, k, N-1);
}   
private void reverse(int[] arr, int l, int r) {
    while (l < r) {
        int t = arr[l];
        arr[l++] = arr[r];
        arr[r--] = t;
    }
}

複雜度分析

  • 時間複雜度:O(n)O(n)
  • 空間複雜度:O(1)O(1)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章