高频面试算法题 | 轮转数组(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,则不需要进行任何反转操作。可以在函数开头添加相应的检查,提前返回。

三、结语

再见!

相关推荐
牛马baby4 分钟前
Java高频面试之并发编程-17
java·开发语言·面试
Coding的叶子4 分钟前
React Flow 简介:构建交互式流程图的最佳工具
前端·react.js·流程图·fgai·react agent
强盛小灵通专卖员2 小时前
分类分割详细指标说明
人工智能·深度学习·算法·机器学习
apcipot_rain5 小时前
【应用密码学】实验五 公钥密码2——ECC
前端·数据库·python
ShallowLin5 小时前
vue3学习——组合式 API:生命周期钩子
前端·javascript·vue.js
Nejosi_念旧5 小时前
Vue API 、element-plus自动导入插件
前端·javascript·vue.js
互联网搬砖老肖5 小时前
Web 架构之攻击应急方案
前端·架构
IT猿手6 小时前
基于强化学习 Q-learning 算法求解城市场景下无人机三维路径规划研究,提供完整MATLAB代码
神经网络·算法·matlab·人机交互·无人机·强化学习·无人机三维路径规划
pixle06 小时前
Vue3 Echarts 3D饼图(3D环形图)实现讲解附带源码
前端·3d·echarts
麻芝汤圆7 小时前
MapReduce 入门实战:WordCount 程序
大数据·前端·javascript·ajax·spark·mapreduce