当前位置: 首页 > news >正文

四种方法解决——力扣189.轮转数组

力扣189.轮转数组

在这里插入图片描述


数组轮转题目全解析:四种方法解决 Rotate Array(含 Java 实现与复杂度分析)


摘要

本文详细解析了 LeetCode 热门题目「旋转数组」(Rotate Array)。我们将从直观到高效介绍四种解法:暴力法、额外数组法、翻转法、环状替换法,逐一讲解思路、优缺点,并给出完整的 Java 实现与复杂度分析。最后总结推荐解法及面试答题策略,帮助读者快速掌握该题。


正文

一、题目描述

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

示例:

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

提示:

  • 1 <= nums.length <= 10^5
  • -2^31 <= nums[i] <= 2^31 - 1
  • 0 <= k <= 10^5

进阶:
请尽可能提出多种解决方案,并实现原地 O(1) 空间的算法。


二、思路分析

  1. 暴力法:每次右移 1 步,重复 k 次,直观但效率低。
  2. 额外数组法:借助临时数组存储旋转结果,再拷贝回原数组,时间 O(n),空间 O(n)。
  3. 翻转法:通过三次反转(整体翻转 + 前 k 个翻转 + 后 n-k 个翻转)实现原地 O(1) 解法,是推荐方案。
  4. 环状替换:基于元素下标循环移动,时间 O(n),空间 O(1),实现稍复杂,但能展示数学思路。

三、解法一:暴力法

思路
每次将最后一个元素取出,插入到数组最前端,其他元素整体后移一位,重复 k 次。

代码实现

public void rotateBruteForce(int[] nums, int k) {int n = nums.length;for (int step = 0; step < k; step++) {// 每一步将最后一个元素移动到最前面,其余右移一位int last = nums[n - 1];for (int i = n - 1; i > 0; i--) {nums[i] = nums[i - 1];}nums[0] = last;}}

复杂度

  • 时间复杂度:O(n·k)
  • 空间复杂度:O(1)

缺点:当 n 和 k 很大时效率过低。


四、解法二:额外数组法

思路
将数组元素按照旋转后的顺序放入一个新数组,再复制回原数组。

代码实现

 public void rotateWithExtraArray(int[] nums, int k) {int n = nums.length;int[] tmp = new int[n];// 将 nums 的最后 k 个放在最前面,其他顺序跟随int start = n - k; // 从 nums[start] 开始复制到 tmp[0]for (int i = 0; i < n; i++) {tmp[i] = nums[(start + i) % n];}// 拷贝回原数组for (int i = 0; i < n; i++) {nums[i] = tmp[i];}}

复杂度

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

优点是直观易懂,但额外空间较大。


五、解法三:翻转法(推荐)

思路

  1. 翻转整个数组;
  2. 翻转前 k 个元素;
  3. 翻转后 n-k 个元素。

代码实现

public void rotateByReversals(int[] nums, int k) {int n = nums.length;// 1. 翻转整个数组reverse(nums, 0, n - 1);// 2. 翻转前 k 个元素(现在是原数组的最后 k 个)reverse(nums, 0, k - 1);// 3. 翻转剩余 n-k 个元素reverse(nums, k, n - 1);}private void reverse(int[] arr, int left, int right) {while (left < right) {int tmp = arr[left];arr[left++] = arr[right];arr[right--] = tmp;}}

复杂度

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

这是最常用的解法,也是面试中推荐优先写出的方案。


六、解法四:环状替换法

思路
从下标 0 开始,把元素放到它应该在的位置,再继续替换下一位置的元素,直到形成环,之后从下一个未处理的元素开始新的环,直到所有元素移动完成。

代码实现

public void rotateByCyclicReplacements(int[] nums, int k) {int n = nums.length;int countMoved = 0; // 已经移动的元素个数for (int start = 0; countMoved < n; start++) {int current = start;int prevValue = nums[start];do {int nextIndex = (current + k) % n;int temp = nums[nextIndex];nums[nextIndex] = prevValue;prevValue = temp;current = nextIndex;countMoved++;} while (start != current);// 开始下一个循环组}}

复杂度

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

实现略复杂,但更贴近数学规律。


七、代码汇总

完整可运行类:

public class Solution {/*** 主入口:默认使用原地翻转法(O(n) 时间,O(1) 空间)。* 如果你想测试其他方法,可以把下面一行替换成调用其它实现:*     rotateWithExtraArray(nums, k);*     rotateBruteForce(nums, k);*     rotateByCyclicReplacements(nums, k);*/public void rotate(int[] nums, int k) {if (nums == null || nums.length <= 1) return;int n = nums.length;k = k % n;if (k == 0) return;// 默认:翻转法(推荐)rotateByReversals(nums, k);}/* ===========================方法1:暴力法(逐步右移)时间 O(n * k),空间 O(1)仅做学习示例,不推荐在大数据上使用=========================== */public void rotateBruteForce(int[] nums, int k) {int n = nums.length;for (int step = 0; step < k; step++) {// 每一步将最后一个元素移动到最前面,其余右移一位int last = nums[n - 1];for (int i = n - 1; i > 0; i--) {nums[i] = nums[i - 1];}nums[0] = last;}}/* ===========================方法2:使用额外数组(临时数组)时间 O(n),空间 O(n)简洁直观=========================== */public void rotateWithExtraArray(int[] nums, int k) {int n = nums.length;int[] tmp = new int[n];// 将 nums 的最后 k 个放在最前面,其他顺序跟随int start = n - k; // 从 nums[start] 开始复制到 tmp[0]for (int i = 0; i < n; i++) {tmp[i] = nums[(start + i) % n];}// 拷贝回原数组for (int i = 0; i < n; i++) {nums[i] = tmp[i];}}/* ===========================方法3:翻转法(三次反转)时间 O(n),空间 O(1)推荐:简单、原地、稳定思路:整体翻转 -> 翻转前 k 个 -> 翻转剩余 n-k 个=========================== */public void rotateByReversals(int[] nums, int k) {int n = nums.length;// 1. 翻转整个数组reverse(nums, 0, n - 1);// 2. 翻转前 k 个元素(现在是原数组的最后 k 个)reverse(nums, 0, k - 1);// 3. 翻转剩余 n-k 个元素reverse(nums, k, n - 1);}private void reverse(int[] arr, int left, int right) {while (left < right) {int tmp = arr[left];arr[left++] = arr[right];arr[right--] = tmp;}}/* ===========================方法4:环状替换(基于 GCD,原地)时间 O(n),空间 O(1)通过计算循环节来逐组替换元素=========================== */public void rotateByCyclicReplacements(int[] nums, int k) {int n = nums.length;int countMoved = 0; // 已经移动的元素个数for (int start = 0; countMoved < n; start++) {int current = start;int prevValue = nums[start];do {int nextIndex = (current + k) % n;int temp = nums[nextIndex];nums[nextIndex] = prevValue;prevValue = temp;current = nextIndex;countMoved++;} while (start != current);// 开始下一个循环组}}
}

八、总结

  • 方法一:暴力法,直观但效率低。
  • 方法二:额外数组法,代码简洁但需 O(n) 额外空间。
  • 方法三:翻转法,时间 O(n)、空间 O(1),推荐作为默认解法。
  • 方法四:环状替换法,数学性更强,适合展示思维深度。

面试建议:优先写翻转法,若时间允许可补充环状替换以展示全面性。


http://www.dtcms.com/a/403080.html

相关文章:

  • ⸢ 伍-Ⅱ⸥ ⤳ 默认安全治理实践:水平越权检测 前端安全防控
  • 力扣856
  • Leetcode94.二叉数的中序遍历练习
  • 多种解法全解析——力扣217. 存在重复元素
  • 用python做的网站多吗二手书交易网站策划书
  • phpcms网站源码ui培训班多少钱
  • Android Studio 导入 opencv
  • 在线网站做成appdede网站地图样式修改
  • 全新尚界H5凭借HUAWEI XMC数字底盘引擎技术,让湿滑路面也“稳”操胜券
  • iOS 26 性能测试实战,如何评估启动速度、CPUGPU 负载、帧率与系统资源适配(uni-app 与 iOS 原生应用性能方案)
  • 腾讯会议→微课操作
  • html原生表格,实现左侧列固定
  • Idea提高开发效率的快捷键最佳学习方式
  • 做网站一定需要icp么中国建设协会官网
  • Selenium使用教程
  • 多线程——单例模式
  • 镜头调焦的 调整的是焦距还是像距?
  • (四)React+.Net+Typescript全栈(错误处理)
  • @ant-design/icons-vue 打包成多格式库
  • 什么是营销型网站?杭州建设教育网站
  • C++开发环境(VSCode + CMake + gdb)
  • JAVA CodeX精选实用代码示例
  • 肥东网站建设南京医院网站建设
  • Qt 多线程解析
  • ZooKeeper与Kafka分布式:从基础原理到集群部署
  • 免费网站服务器安全软件下载wordpress权限设置方法
  • three.js射线拾取点击位置与屏幕坐标映射
  • AutoMQ × Ververica:打造云原生实时数据流最佳实践!
  • Laravel5.8 使用 snappyPDF 生成PDF文件
  • 自己做网站的图片手机芒果tv2016旧版