高频面试算法题 | 轮转数组(JavaScript最优解)

一、题目描述

给定一个整数数组 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;
    }
}

核心思想

这个题解的核心思想是利用反转数组的操作来达到旋转数组的目的。 具体来说,它基于以下观察结果:

  1. 将整个数组反转。
  2. 将数组的前 k 个元素反转。
  3. 将数组的剩余元素(从索引 k 到结尾)反转。

这三个步骤完成后,数组就被正确地旋转了 k 个位置。

详细方案

  1. 处理 k 值:

    • const n = nums.length;: 获取数组的长度。
    • k = k % n;: 将 k 规范化到 0n-1 的范围内。 这确保了 k 不会超出数组的边界。 例如,如果 k2n + 3,那么它等价于旋转 3 个位置。
    • if (k === 0) { return; }:这是一个优化,如果 k 是 0,则不需要进行任何操作,直接返回。
  2. 反转整个数组:

    • reverse(nums, 0, n - 1);: 调用 reverse 函数,将整个数组 nums 从索引 0n - 1 反转。
  3. 反转前 k 个元素:

    • reverse(nums, 0, k - 1);: 调用 reverse 函数,将数组的前 k 个元素(从索引 0k - 1)反转。
  4. 反转剩余元素:

    • reverse(nums, k, n - 1);: 调用 reverse 函数,将数组剩余的元素(从索引 kn - 1)反转。
  5. 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),这是一个原地算法,只需要常量级别的额外空间。

注意事项

  1. 原地算法: 这个算法是原地算法,直接修改输入的数组 nums,不需要创建额外的数组。 如果题目明确要求不能修改原数组,则不能使用此方法。
  2. k 规范化: 务必对 k 进行规范化,确保它在有效范围内。 否则,可能会出现数组越界错误。题目中已经规范化,但是如果直接使用,需要考虑k的规范化.
  3. reverse 函数的范围: 确保 reverse 函数的 startend 参数正确,防止数组越界。
  4. **代码可读性:**虽然代码很简洁,可以添加适当的注释,解释每个步骤的作用.现在已经有了。
  5. 性能考虑: 虽然这个算法的时间复杂度是 O(n),但在实际应用中,其性能往往优于其他算法(例如使用额外的数组)。 这是因为原地操作可以减少内存分配和复制的开销。
  6. 数组长度为0或者1的情况: 如果数组长度为0或者1,则不需要进行任何反转操作。可以在函数开头添加相应的检查,提前返回。

三、结语

再见!

相关推荐
Go_going_8 分钟前
【解决 el-table 树形数据更新后视图不刷新的问题】
前端·javascript·vue.js
进取星辰18 分钟前
10、Context:跨维度传音术——React 19 状态共享
前端·react.js·前端框架
wfsm20 分钟前
react使用01
前端·javascript·react.js
小小小小宇31 分钟前
Vue 3 的批量更新机制
前端
阳光普照世界和平35 分钟前
从单点突破到链式攻击:XSS 的渗透全路径解析
前端·web安全·xss
海码0071 小时前
【Hot100】 73. 矩阵置零
c++·线性代数·算法·矩阵·hot100
MrsBaek1 小时前
前端笔记-AJAX
前端·笔记·ajax
前端Hardy1 小时前
第4课:函数基础——JS的“魔法咒语”
前端·javascript
无心水1 小时前
【Java面试笔记:基础】6.动态代理是基于什么原理?
java·笔记·面试·动态代理·cglib·jdk动态代理
孟陬1 小时前
如何检测 Network 请求异常 - PerformanceObserver
前端