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

三、结语

再见!

相关推荐
大家的林语冰19 分钟前
✌️ Deno 2.9 来了,ESM 直接导入 CSS,甚至能开发桌面应用?!
前端·javascript·node.js
米尔的可达鸭1 小时前
深入操作系统 Socket 底层:套接字控制块、FD映射、阻塞IO核心完整实现
arm开发·数据结构·websocket·网络协议·算法·架构·安全架构
不简说1 小时前
JS 代码技巧 vol.1 — 10 个让代码少写 30% 的小套路
前端·javascript·面试
Hilaku1 小时前
为什么业务型前端容易遭遇中年危机?
前端·javascript·程序员
tachibana21 小时前
hot100 课程表(207)
java·数据结构·算法·leetcode
神仙别闹1 小时前
编写基于C++ Huffman 算法的无损压缩程序和解压程序
java·c++·算法
এ慕ོ冬℘゜1 小时前
深度解析 JavaScript:赋予 Web 灵魂的“全栈语言”
开发语言·前端·javascript
iCOD3R1 小时前
Skill - 3秒生成精美GitHub主页
前端·程序员·ai编程
Mr_凌宇1 小时前
# AI 应用工程入门学习笔记 一
人工智能·面试·程序员
大鱼>1 小时前
宠物活动轨迹追踪系统:GPS/BDS+UWB+BLE多定位融合方案
人工智能·深度学习·算法·iot·宠物