跳到主要内容

189. 轮转数组

1. 题目简介​

难度:中等,原题链接:189. 轮转数组。

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

示例 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. 问题分析​

向右轮转 k 位,就是把末尾的 k 个元素移到开头,同时保持两部分各自的顺序。数组长度为 n 时,轮转 n 位会回到原样,所以先用 k %= n 去掉完整的轮转圈数。

2.1 模拟算法​

可以用临时数组保存末尾的 k 个元素,再完成搬移:

  1. 保存末尾的 k 个元素。
  2. 从后往前,把前 n - k 个元素各右移 k 位。这个方向能避免覆盖尚未搬走的元素。
  3. 把临时数组中的元素写回开头的 k 个位置。

这种方法需要 O(k) 的额外空间。

2.2 分段反转​

题目还提出了空间上的进阶要求:

你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?

可以把数组分成前 n - k 个元素和后 k 个元素,分别反转两部分,再反转整个数组。最后一次反转会交换两部分的位置,同时恢复各自内部的顺序。

以 [1, 2, 3, 4, 5, 6, 7]、k = 3 为例:

  1. 分别反转 [1, 2, 3, 4] 和 [5, 6, 7],得到 [4, 3, 2, 1, 7, 6, 5]。
  2. 反转整个数组,得到 [5, 6, 7, 1, 2, 3, 4]。

也可以用下标验证。原下标 i < n - k 的元素,第一次反转后位于 n - k - 1 - i,整体反转后位于 i + k。后半段的元素最终位于 i + k - n。两种情况都符合向右轮转后的目标下标 (i + k) % n。

3. 代码实现​

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

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

reverse 反转闭区间 [start, end],三次调用分别对应前 n - k 个元素、后 k 个元素和整个数组。k 为 0 时,第二次调用的区间为空,start < end 不成立,不会改动数组。

4. 复杂度分析​

  • 时间复杂度:O(n)。
  • 空间复杂度:O(1)。

来源:Coding 101 原文 · 免费阅读,引用请保留出处。内容使用说明