轮转数组解决方法

轮转数组

问题描述

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。也就是说,将数组的每个元素向右移动 k 个位置,超过数组长度的部分循环到数组的开头。

示例:

  • 输入:nums = [1,2,3,4,5,6,7], k = 3
  • 输出:[5,6,7,1,2,3,4]

方法一:使用额外的数组

思路

  • 创建一个与原数组大小相同的额外数组。
  • 将原数组的元素按照旋转后的索引位置放入新数组中。
  • 将新数组的值复制回原数组。

图解

假设 nums = [1,2,3,4,5,6,7], k = 3

  • 计算新位置:对于每个索引 i,新位置为 (i + k) % n,其中 n 是数组长度。
原索引 i原元素 nums[i]新索引 (i + k) % n放入位置
01(0 + 3) % 7 = 33
12(1 + 3) % 7 = 44
23(2 + 3) % 7 = 55
34(3 + 3) % 7 = 66
45(4 + 3) % 7 = 00
56(5 + 3) % 7 = 11
67(6 + 3) % 7 = 22
  • 新数组为 [5,6,7,1,2,3,4]

实现代码

function rotateUsingExtraArray(nums: number[], k: number): void {const n = nums.length;k = k % n; // 如果 k 大于数组长度,取模const rotatedArray = new Array(n);// 将旋转后的元素放入新数组for (let i = 0; i < n; i++) {rotatedArray[(i + k) % n] = nums[i];}// 将新数组的值复制回原数组for (let i = 0; i < n; i++) {nums[i] = rotatedArray[i];}
}

复杂度分析

  • 时间复杂度:O(n),需要遍历数组两次。
  • 空间复杂度:O(n),需要额外的数组存储旋转后的结果。

方法二:使用数组反转

思路

  • 通过反转数组的部分或全部来实现旋转。
  • 具体步骤:
    1. 反转整个数组。
    2. 反转前 k 个元素。
    3. 反转剩余的元素。

图解

nums = [1,2,3,4,5,6,7], k = 3 为例:

  1. 反转整个数组

    [1,2,3,4,5,6,7][7,6,5,4,3,2,1]

  2. 反转前 k 个元素

    [7,6,5,4,3,2,1][5,6,7,4,3,2,1]

  3. 反转剩余的元素

    [5,6,7,4,3,2,1][5,6,7,1,2,3,4]

实现代码

function rotateUsingReverse(nums: number[], k: number): void {const n = nums.length;k = k % n; // 如果 k 大于数组长度,取模// 定义反转函数function reverse(start: number, end: number): void {while (start < end) {[nums[start], nums[end]] = [nums[end], nums[start]]; // 交换元素start++;end--;}}// 反转整个数组reverse(0, n - 1);// 反转前 k 个元素reverse(0, k - 1);// 反转剩余的元素reverse(k, n - 1);
}

复杂度分析

  • 时间复杂度:O(n),反转操作需要遍历数组三次,每次都是 O(n)。
  • 空间复杂度:O(1),只使用了常数级别的额外空间。

解释

  • 为什么这种方法有效?

    • 反转整个数组后,原来的末尾 k 个元素移动到了数组的开头,但顺序是反的。
    • 通过再次反转前 k 个元素,将它们的顺序调整过来。
    • 同理,反转剩余的元素,也恢复它们的顺序。

方法三:使用循环替换(环状替换)

思路

  • 直接将每个元素放到它最终的位置,但需要注意可能会覆盖尚未移动的元素。
  • 通过循环替换元素,避免数据被覆盖。

实现步骤

  1. 初始化计数器 count = 0,记录已经移动的元素数量。
  2. 从索引 start = 0 开始,进行循环替换:
    • 保存当前位置的值 prevValue
    • 计算下一个位置的索引 next = (current + k) % n
    • prevValue 放到 next 位置,保存 nums[next] 为新的 prevValue
    • 更新 current = next
    • 增加 count,直到 count 等于数组长度 n,表示所有元素都已移动。
  3. 如果出现循环,需要从下一个位置开始新的循环。

注意

  • 当数组长度和 k 的最大公约数不为 1 时,会出现循环,需要从下一个起始位置继续。

实现代码

function rotateUsingCyclicReplacement(nums: number[], k: number): void {const n = nums.length;k = k % n; // 如果 k 大于数组长度,取模let count = 0; // 记录已经移动的元素个数for (let start = 0; count < n; start++) {let current = start;let prevValue = nums[start];do {const next = (current + k) % n;const temp = nums[next];nums[next] = prevValue;prevValue = temp;current = next;count++;} while (start !== current);}
}

复杂度分析

  • 时间复杂度:O(n),每个元素只移动一次。
  • 空间复杂度:O(1),只使用了常数级别的额外空间。

解释

  • 为什么需要 count?

    • 因为可能会出现环状替换,一个循环无法覆盖所有元素,所以需要计数。
  • 示例演示:

    • nums = [1,2,3,4,5,6], k = 2 为例。

    • 第一轮循环:

      • start = 0current = 0prevValue = nums[0] = 1
      • next = (0 + 2) % 6 = 2
      • nums[2] 被替换为 1prevValue 更新为 nums[2] 的原值 3
      • current 更新为 2count = 1
      • 重复上述步骤,直到 current 回到 start
    • 第二轮循环:

      • start = 1,重复类似的过程。

总结

  • 方法一适用于不考虑空间复杂度的情况,理解起来较为直观。
  • 方法二通过数组反转,实现原地旋转,空间复杂度低,性能优良。
  • 方法三在空间复杂度为 O(1) 的情况下,时间效率较高,但理解起来稍微复杂。

测试代码

你可以使用以下测试代码来验证上述方法的正确性:

function testRotateFunctions() {const testCases = [{ nums: [1, 2, 3, 4, 5, 6, 7], k: 3, expected: [5, 6, 7, 1, 2, 3, 4] },{ nums: [-1, -100, 3, 99], k: 2, expected: [3, 99, -1, -100] },];for (const { nums, k, expected } of testCases) {const nums1 = nums.slice();rotateUsingExtraArray(nums1, k);console.assert(nums1.toString() === expected.toString(), `rotateUsingExtraArray failed for input ${nums}, k=${k}`);const nums2 = nums.slice();rotateUsingReverse(nums2, k);console.assert(nums2.toString() === expected.toString(), `rotateUsingReverse failed for input ${nums}, k=${k}`);const nums3 = nums.slice();rotateUsingCyclicReplacement(nums3, k);console.assert(nums3.toString() === expected.toString(), `rotateUsingCyclicReplacement failed for input ${nums}, k=${k}`);}console.log("All test cases passed!");
}testRotateFunctions();

结论

  • 方法一(使用额外的数组):理解简单,但需要额外的空间,空间复杂度为 O(n)。
  • 方法二(使用数组反转):不需要额外空间,空间复杂度为 O(1),是实际应用中较为推荐的方法。
  • 方法三(使用循环替换):也不需要额外空间,空间复杂度为 O(1),但理解和实现相对复杂。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/56396.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

新能源行业必会基础知识-----电力交易员职业标准-----持续更新

新能源行业知识体系-------主目录-----持续更新https://blog.csdn.net/grd_java/article/details/140004020 文章目录 1. 基本常识2. 达到基本入行标准&#xff08;四级/中级&#xff09;2.1 交易资质及信息管理2.2 中长期交易2.3 现货交易2.4 辅助服务管理2.5 售电管理2.6 电价…

mysql数据迁移到elasticsearch以及elasticsearch的使用

目录 根据数据不断调整架构安装elasticsearch 版本8.12.2kibana安装ik分词分词的拓展以及停用 springboot实战pom.xmlapplication.yml相关配置框架集成-SpringData-集成测试-文档操作 相关代码调整 随着物联网平台的不断发展&#xff0c;平台要求接入的模块会越来越多&#xff…

Qt 实现动态时钟

1.实现效果 2.widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE namespace

leetcode中哈希的python解法:Counter()介绍

Counter 是 Python 的 collections 模块中的一个类&#xff0c;用于统计可迭代对象中元素的出现次数。Counter 是一种专门为计数设计的哈希表&#xff08;字典&#xff09;&#xff0c;它的键是元素&#xff0c;值是元素出现的次数。 Counter 的特点&#xff1a; 继承自 dict…

hackmyvm-Hundred靶机

主机发现 sudo arp-scan -l 以sudo权限执行arp-scan -l 扫描并列出本地存在的机器&#xff0c;发现靶机ip为192.168.91.153 nmap扫描 端口发现 21/tcp open ftp 22/tcp open ssh 80/tcp open http web信息收集 我们先尝试一下ftp端口的匿名登录 FTP:是文件传输协议的端…

JAVA 中的克隆对象

克隆对象就是复制一个一模一样的对象&#xff0c;但是复制出来的对象和原对象不是同一个对象&#xff0c;是两个对象&#xff0c;只不过复制过来的对象和原对象除了内存地址之外&#xff0c;其它的属性一模一样。 在超类 Object 中有一个 clone() 方法&#xff1a; protected…

17. typedef关键字的使用

一、为什么需要typedef关键字 C 语言允许用户使用 typedef 来为一个数据类型起一个新的别名。一旦用户在程序中定义了别名&#xff0c;就可以在该程序中使用别名来定义变量的类型、数组的类型、指针变量的类型与函数的类型等。 typedef 关键字定义的名称并不是真的创造了一种数…

循序渐进丨MogDB 中 gs_dump 数据库导出工具源码概览

背景 gs_dump 是 MogDB 中一个功能丰富灵活的数据库导出工具&#xff0c;在数据库的维护、迁移和开发中经常使用。该工具允许用户根据需要导出整个数据库或者数据库中的特定对象&#xff0c;如模式&#xff08;schema&#xff09;、表&#xff08;tables&#xff09;、视图&am…

grafana version 11.1.0 设置Y轴刻度为1

grafana 版本 # /usr/share/grafana/bin/grafana --version grafana version 11.1.0设置轴 Axis 搜索 Standard options 在"Decimals"中输入0&#xff0c;确保只显示整数

Java基础12-特殊文件和日志技术

十二、特殊文件和日志技术 1、特殊文件 properties&#xff1a;用来存储键值对数据。 xml&#xff1a;用来存储有关系的数据。 1.1 properties文件 特点&#xff1a;存储键值对&#xff0c;键不能重复&#xff0c;文件后缀一般是.properties结尾的。 properties&#xff1a;是…

混合专家模型(MoE)中的容量因子f

在混合专家模型&#xff08;MoE&#xff09;中&#xff0c;容量因子f是一个重要的参数&#xff0c;它用于衡量MoE层中专家模型的容量和利用情况。具体来说&#xff0c;容量因子f通常定义为MoE层中实际激活的专家数量与MoE层中总专家数量的比例。 当容量因子f为1.25时&#xff…

自定义注解和组件扫描在Spring Boot中动态注册Bean(一)

​ 博客主页: 南来_北往 系列专栏&#xff1a;Spring Boot实战 在Spring Boot中&#xff0c;自定义注解和组件扫描是两种强大的机制&#xff0c;它们允许开发者以声明性的方式动态注册Bean。这种方式不仅提高了代码的可读性和可维护性&#xff0c;还使得Spring Boot应用的…

UPDATE 更新数据

1.更新某一列字段的值 以 teacher 表为例&#xff0c;需要把前 3 条数据的 age 更新为 33&#xff0c;命令如下: UPDATE teacher SET age18,id_number44444444440604099X WHERE id 30;执行结果如下图 : 这里是对 age 字段列的前三条值进行数据的更新&#xff0c;注意更新…

网络安全有关法律法规

1. 前言 在当今数字化高速发展的时代&#xff0c;网络安全已成为关乎国家、企业和个人的重要议题。为了应对日益复杂的网络安全挑战&#xff0c;一系列网络安全法律法规应运而生&#xff0c;它们如同坚实的盾牌&#xff0c;守护着我们的数字世界。现在是2024年10月&#xff0c…

Unity学习日志-API

Untiy基本API 角度旋转自转相对于某一个轴 转多少度相对于某一个点转练习 角度 this.transform.rotation(四元数)界面上的xyz(相对于世界坐标) this.transform.eulerAngles;相对于父对象 this.transform.localEulerAngles;设置角度和设置位置一样&#xff0c;不能单独设置xz…

PHP对Json数据格式的理解

PHP 对 JSON&#xff08;JavaScript Object Notation&#xff09;数据格式的理解涉及将 JSON 数据解析为 PHP 变量&#xff08;通常是数组或对象&#xff09;&#xff0c;以及将 PHP 变量编码为 JSON 字符串。JSON 是一种轻量级的数据交换格式&#xff0c;易于人阅读和编写&…

EM算法学习

1.EM算法的介绍 可以发现&#xff1a;计算出θA和θB的值的前提是知道A、B币种的抛掷情况。 所以我们需要使用EM算法&#xff1a;求出每轮选择硬币种类的概率 2.EM算法执行过程&#xff1a; 第一步&#xff1a;首先初始化设置一组PA和PB证明的值。然后通过最大似然估计得到每…

MOE论文详解(3)-Switch Transformers

Switch Transformers也是google在2022年发表的一篇论文, 该论文简化了MoE的路由算法, 减少了计算量和通信量; 第一次支持bfloat16精度进行训练. 基于T5-Base和T5-Large设计的模型在相同的算力下训练速度提升了7x倍; 同时发布了1.6万亿(1.6 trillion)参数的MoE模型&#xff0c;相…

Linux系统:Ubuntu上安装Chrome浏览器

Ubuntu系统版本&#xff1a;23.04 在Ubuntu系统上安装Google Chrome浏览器&#xff0c;可以通过以下步骤进行&#xff1a; 终端输入以下命令&#xff0c;先更新软件源&#xff1a; sudo apt update 或 sudo apt upgrade终端输入以下命令&#xff0c;下载最新的Google Chrome .…

多机编队—(3)Fast_planner无人机模型替换为Turtlebot3模型实现无地图的轨迹规划

文章目录 前言一、模型替换二、Riz可视化三、坐标变换四、轨迹规划最后 前言 前段时间已经成功将Fast_planner配置到ubuntu机器人中&#xff0c;这段时间将Fast_planner中的无人机模型替换为了Turtlebot3_waffle模型&#xff0c;机器人识别到环境中的三维障碍物信息&#xff0…