题目描述
给定一个整数数组 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 - 10 <= 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);
};
设数组长度为 ,轮转步数为 。我们需要将数组分为两部分 和 : 是前 个元素, 是后 个元素。目标是将 变为 。
- 反转整个数组:
- 反转前 个元素:
- 反转后 个元素:
首先是简化轮转步数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个元素,完成题目
时间,空间复杂度
时间复杂度: (三次遍历数组)
- 第一次反转: 整个数组,长度为 。复杂度 。
- 第二次反转: 前 个元素,长度为 。复杂度 。 (k'=k%n)
- 第三次反转: 后 个元素,长度为 。复杂度 。
三次反转的总时间复杂度为:
。
空间复杂度: (只使用了常数级别的额外变量)