一、题目描述
给定一个整数数组 nums
,将数组中的元素向右轮转 k
**个位置,其中 k
**是非负数。
示例 1:
ini
输入: 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:
ini
输入: 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 - 1
0 <= k <= 105
二、题解
本人题解(有待优化)
js
/**
* @param {number[]} nums
* @param {number} k
* @return {void} Do not return anything, modify nums in-place instead.
*/
var rotate = function(nums, k) {
let len=nums.length;
const ansMap=new Map();
const ans=[];
for(let i=0;i<len;i++){
ansMap.set((i+k)%len,nums[i]);
}
for(let i=0;i<len;i++){
if(ansMap.has(i)){
nums[i]=ansMap.get(i)
}
}
};
该题解时间复杂度和空间复杂度较高,需要优化
正解(最优解)
js
/**
* @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;
k = k % n;
if (k === 0) {
return; // 为零判断
}
reverse(nums, 0, n - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, n - 1);
};
function reverse(nums, start, end) {
// 提取循环条件中使用的常量表达式
const mid = Math.floor((end - start) / 2); //仅计算一次mid
for (let i = 0; i <= mid; ++i) {
//使用i来计算索引
const temp = nums[start + i];
nums[start + i] = nums[end - i];
nums[end - i] = temp;
}
}
核心思想
这个题解的核心思想是利用反转数组的操作来达到旋转数组的目的。 具体来说,它基于以下观察结果:
- 将整个数组反转。
- 将数组的前
k
个元素反转。 - 将数组的剩余元素(从索引
k
到结尾)反转。
这三个步骤完成后,数组就被正确地旋转了 k
个位置。
详细方案
-
处理
k
值:const n = nums.length;
: 获取数组的长度。k = k % n;
: 将k
规范化到0
到n-1
的范围内。 这确保了k
不会超出数组的边界。 例如,如果k
是2n + 3
,那么它等价于旋转3
个位置。if (k === 0) { return; }
:这是一个优化,如果k
是 0,则不需要进行任何操作,直接返回。
-
反转整个数组:
reverse(nums, 0, n - 1);
: 调用reverse
函数,将整个数组nums
从索引0
到n - 1
反转。
-
反转前
k
个元素:reverse(nums, 0, k - 1);
: 调用reverse
函数,将数组的前k
个元素(从索引0
到k - 1
)反转。
-
反转剩余元素:
reverse(nums, k, n - 1);
: 调用reverse
函数,将数组剩余的元素(从索引k
到n - 1
)反转。
-
reverse
函数的实现:const mid = Math.floor((end - start) / 2);
: 计算需要交换的次数。只需要遍历数组的一半即可完成反转。for (let i = 0; i <= mid; ++i)
: 循环遍历需要反转的部分。const temp = nums[start + i];
: 保存start + i
位置的元素到一个临时变量temp
中。nums[start + i] = nums[end - i];
: 将end - i
位置的元素移动到start + i
位置。nums[end - i] = temp;
: 将临时变量temp
中保存的原始start + i
位置的元素移动到end - i
位置。
复杂度分析
- 时间复杂度: O(n),其中 n 是数组的长度。
reverse
函数遍历数组的固定部分,总共调用三次。 - 空间复杂度: O(1),这是一个原地算法,只需要常量级别的额外空间。
注意事项
- 原地算法: 这个算法是原地算法,直接修改输入的数组
nums
,不需要创建额外的数组。 如果题目明确要求不能修改原数组,则不能使用此方法。 k
规范化: 务必对k
进行规范化,确保它在有效范围内。 否则,可能会出现数组越界错误。题目中已经规范化,但是如果直接使用,需要考虑k的规范化.reverse
函数的范围: 确保reverse
函数的start
和end
参数正确,防止数组越界。- **代码可读性:**虽然代码很简洁,可以添加适当的注释,解释每个步骤的作用.现在已经有了。
- 性能考虑: 虽然这个算法的时间复杂度是 O(n),但在实际应用中,其性能往往优于其他算法(例如使用额外的数组)。 这是因为原地操作可以减少内存分配和复制的开销。
- 数组长度为0或者1的情况: 如果数组长度为0或者1,则不需要进行任何反转操作。可以在函数开头添加相应的检查,提前返回。
三、结语
再见!