携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情
题目描述
给你一个数组,将数组中的元素向右轮转 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:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]
提示:
1 <= nums.length <= 105-231 <= nums[i] <= 231 - 10 <= k <= 105
进阶:
- 尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。
- 你可以使用空间复杂度为
O(1)的 原地 算法解决这个问题吗?
解题思路——插入法
[1,2,3,4,5,6,7],k=3 时,可以看做是向右移动 k 位。假设一次移动一位,我们要移动 k 次。我们从最后一个元素来看,向右移动一位相当于把它变成第一个元素,当它变成第一个元素之后,其他元素的相对位置就都向右移动了一位,那么我通过循环,每次都将新的最后一个元素插入到数组头部,是不是就相当于数组整体向右轮转 k 次了?
题解
var rotate = function(nums, k) {
while(k--) {
nums.unshift(nums.pop());
}
return nums;
};
解题思路——位置计算法
还是拿 [1,2,3,4,5,6,7],k=3 来说,轮转后的数组为 [5,6,7,1,2,3,4]。
观察一下这两个数组,发现了什么?
是不是原本第 0 个位置的元素跑第 k 个了?
那第 1 个位置的元素呢?是不是跑第 1 + k 个去了?
那最后一个元素呢?是不是越界了?
对没错,最后一个元素这么算就越界了,为了让它不越界,我们需要对下标进行取模操作,(i+k)%nums.length 。
所以数组的每个元素在轮转 k 次之后的位置就是 (i+k)%nums.length 。
但是如果我们直接对原数组进行操作的话,会覆盖掉原本位置的值,污染数据,所以需要另外开辟一个数组来记录轮转之后的数组,最后将新数组赋值给源数组。
题解
var rotate = function(nums, k) {
const lens = nums.length;
const res = [];
for(let i=0; i<lens; ++i) {
res[(i+k)%lens] = nums[i]
}
for(let i=0; i<lens; ++i) {
nums[i] = res[i];
}
return nums;
};
解题思路——翻转法
官方解法中看到这么一种翻转法。
还是拿 [1,2,3,4,5,6,7],k=3 来说:
- 将数组进行整体翻转,得到
[7,6,5,4,3,2,1]。 - 将下标为
[0, k-1]部分的数组进行翻转,得到 [5,6,7,4,3,2,1]。 - 将下标为
[k, length-1]部分的数组进行翻转,得到 [5,6,7,1,2,3,4]。
最后你会发现结果就是数组向右轮转 k 个位置的结果啦。
题解
const reverse = (nums, start, end) => {
while(start < end) {
[nums[start], nums[end]] = [nums[end], nums[start]];
end--;
start++;
}
}
var rotate = function(nums, k) {
const lens = nums.length;
nums.reverse();
k %= lens;
reverse(nums, 0, k-1);
reverse(nums, k, lens-1);
return nums;
};
需要注意的是这里的 k 需要取模,用来处理 nums.length < k 的情况。