LeetCode热题100——189.轮转数组

28 阅读1分钟

题目描述

给定一个整数数组 nums,将数组中的元素向右轮转 k **个位置,其中 k **是非负数。

 

示例:

输入: 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]

 

提示:

  • 1 <= nums.length <= 105
  • -231 <= nums[i] <= 231 - 1
  • 0 <= k <= 105

题解

普通解法

设数组长度为n

观察示例,我们可以通过把数组要轮转到开始位置的部分(下标从n-k到n-1)放到一个新数组 然后再将数组的剩余部分拼接到新数组中,最后用新数组覆盖原数组

很明显,此解法需要O(N)的时间和空间复杂度

下面给出更优的解法

三次反转

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var rotate = function (nums, k) {
    const n = nums.length;
    const steps = k % n;//轮转步数

    if (steps === 0) {
        return;
    }

    // 反转函数
    const reverse = (arr, start, end) => {
        while (start < end) {
            [arr[start], arr[end]] = [arr[end], arr[start]];
            start++;
            end--;
        }
    };

    // 反转整个数组 (A B -> B^T A^T)
    reverse(nums, 0, n - 1);

    // 反转前 k 个元素 (B^T A^T -> B A^T)
    reverse(nums, 0, steps - 1);

    // 反转后 N - k 个元素 (B A^T -> B A)
    reverse(nums, steps, n - 1);
};

设数组长度为 NN,轮转步数为 kk。我们需要将数组分为两部分 AABBAA 是前 NkN-k 个元素,BB 是后 kk 个元素。目标是将 ABA B 变为 BAB A

  1. 反转整个数组: AB(AB)T=BTATA B \rightarrow (A B)^T = B^T A^T
  2. 反转前 kk 个元素: BTAT(BT)TAT=BATB^T A^T \rightarrow (B^T)^T A^T = B A^T
  3. 反转后 NkN-k 个元素: BATB(AT)T=BAB A^T \rightarrow B (A^T)^T = B A

首先是简化轮转步数steps = k % n 如果steps=0,直接返回

定义了一个翻转函数

 const reverse = (arr, start, end) => {
        while (start < end) {
            [arr[start], arr[end]] = [arr[end], arr[start]];
            start++;
            end--;
        }
    };

两个指针start end,分别从开头和末尾开始向中间移动,期间不断交换start和end的值以完成翻转

然后三个步骤分别翻转整个数组、前steps个元素、后n-steps个元素,完成题目

时间,空间复杂度

时间复杂度:O(N)O(N) (三次遍历数组)

  1. 第一次反转: 整个数组,长度为 NN。复杂度 O(N)O(N)
  2. 第二次反转:kk 个元素,长度为 kk'。复杂度 O(k)O(k')。 (k'=k%n)
  3. 第三次反转:NkN-k 个元素,长度为 NkN-k'。复杂度 O(Nk)O(N-k')

三次反转的总时间复杂度为:

O(N)+O(k)+O(Nk)=O(2N)=O(N)O(N) + O(k') + O(N-k') = O(2N) = \mathbf{O(N)}

空间复杂度:O(1)O(1) (只使用了常数级别的额外变量)