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

LeetCode 189 轮转数组:三次反转原地解决,图解 Java 实现

在数组题中,“轮转数组”是一道很典型的题。它的代码并不长,但同时考查了数组下标、空间复杂度、边界处理,以及能否从结果反推出操作过程。

这篇文章用 Java 解决 LeetCode 189「轮转数组」,重点讲清楚一个看起来有些巧妙的方法:为什么只需要三次反转,就能把数组向右轮转k个位置。

一、题目描述

给定一个整数数组nums,将数组中的元素向右轮转k个位置,要求直接修改原数组。

例如:

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

向右轮转 3 个位置,可以理解为:把数组末尾的 3 个元素[5,6,7]移到数组开头,其余元素[1,2,3,4]整体向后移动。

二、最容易想到的做法:使用额外数组

原数组下标为i的元素,轮转后会出现在:

(i + k) % n

因此,可以创建一个长度相同的新数组,把每个元素放入新位置,再复制回原数组:

public void rotate(int[] nums, int k) { int n = nums.length; k %= n; int[] temp = new int[n]; for (int i = 0; i < n; i++) { temp[(i + k) % n] = nums[i]; } System.arraycopy(temp, 0, nums, 0, n); }

这个方法很直观,时间复杂度为O(n),但需要O(n)的额外空间。如果面试官进一步要求空间复杂度为O(1),就需要考虑原地操作。

三、最优解:三次反转

仍然以数组为例:

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

向右轮转 3 位后,希望得到:

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

可以把原数组分成两部分:

A = [1, 2, 3, 4] B = [5, 6, 7]

轮转的目标,本质上就是把A + B变成B + A

具体分三步。

第一步:反转整个数组

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

此时两部分的位置已经交换,但每个部分内部的顺序也被反转了。可以表示为:

reverse(B) + reverse(A)

第二步:反转前 k 个元素

前 3 个元素是[7,6,5],将其反转:

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

这样,原数组末尾的B已经恢复成正确顺序。

第三步:反转剩余元素

再把下标kn - 1的部分[4,3,2,1]反转:

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

最终得到目标结果。

整个过程可以概括为:

整体反转 → 反转前 k 个元素 → 反转后 n-k 个元素

四、为什么三次反转一定成立?

假设原数组由两部分组成:

A + B

其中B是需要移到前面的最后k个元素。

第一次整体反转后得到:

reverse(B) + reverse(A)

然后分别反转前后两部分:

B + A

这正是向右轮转后的结果。因此,三次反转不是只对某个示例有效,而是对任意数组都成立。

五、完整 Java 代码

class Solution { public void rotate(int[] nums, int k) { int n = nums.length; k %= n; // 1. 反转整个数组 reverse(nums, 0, n - 1); // 2. 反转前 k 个元素 reverse(nums, 0, k - 1); // 3. 反转剩余元素 reverse(nums, k, n - 1); } private void reverse(int[] nums, int left, int right) { while (left < right) { int temp = nums[left]; nums[left] = nums[right]; nums[right] = temp; left++; right--; } } }

reverse方法使用双指针:left从左向右移动,right从右向左移动,每次交换两个位置的元素,直到两个指针相遇。

六、为什么必须对 k 取模?

如果数组长度为 7,而k = 10,向右轮转 10 次和向右轮转 3 次的结果相同:

10 % 7 = 3

因为每轮转n次,数组都会恢复原状,所以真正有效的轮转次数是:

k %= nums.length;

这一步还能避免k大于数组长度时,后续反转区间越界。

k = 0时,第二次反转调用的区间是[0, -1]。由于reverse中的条件是left < right,循环不会执行,代码仍然能够正确运行。

七、复杂度分析

虽然进行了三次反转,但每个元素只会被交换有限次,因此:

  • 时间复杂度:O(n)

  • 空间复杂度:O(1)

它没有创建与数组长度相关的辅助空间,满足原地修改的要求。

八、常见错误

1. 忘记对 k 取模

k > nums.length时,反转区间可能越界。进入反转逻辑前,应先执行k %= n

2. 反转顺序写错

向右轮转的常见写法是:

整体 → 前 k 个 → 后 n-k 个

不要把k误写成n - k。可以始终记住:向右轮转后,原数组最后的k个元素要出现在最前面。

3. 逐个移动导致超时

如果每轮转一次,就把整个数组移动一遍,那么时间复杂度是O(n × k)。当数组和k都很大时,这种方法容易超时。

4. 只修改了局部变量

题目要求原地修改nums,不能只让一个新数组变量指向结果而不复制回去。三次反转直接操作原数组,不存在这个问题。

http://www.jsqmd.com/news/1318297/

相关文章:

  • 知行之桥EDI系统邮件通知机制解析与应用实践
  • LeetCode 189 轮转数组|3种解法拆解,从暴力到O(1)原地最优解
  • Python 面向对象进阶——继承、多态、魔术方法
  • Claude Code实战指南:从环境搭建到Skill工具链的AI编程全流程
  • 2026年电磁兼容检测实验室怎么选?厂家推荐与口碑分析 - 优质品牌商家
  • MSFvenom免杀Shellcode生成与监听部署实战指南
  • 从技术原理到实战:揭秘高并发抢票系统的应对策略与技巧
  • 短线、波段与价值投资策略全解析
  • 微型挖机出品质哪家高?2026十大出片品牌深度测评,所见即所得 - 工业品牌热点
  • Open-Golf游戏性能优化实战:从算法到渲染的全面调优指南
  • 卡牌游戏开发的技术困境与Godot框架的模块化解法:从性能瓶颈到规则引擎的完整方案
  • 如何用Diablo Edit2轻松打造你的专属暗黑破坏神2角色?
  • Python循环结构解析与高效编程实践
  • 教室照明设计:如何通过科学用光提升学习效率与视力健康
  • 2026 岳阳往返长沙全攻略 正规车队包车避坑提醒收藏 - 资讯动态
  • 奥特曼也逃不过刷TikTok上瘾,Sora背后最抓马的一段来了
  • 企业 AI Agent 应用场景全景:十大高频落地场景与 ROI 深度分析
  • 并行草稿模型与因果修正:大语言模型推理加速实践指南
  • 电化学氧气传感器原理与Grove模块应用全解析
  • 2026年装修公司推荐:广受好评的服务商服务质量评选 - 工业推荐榜
  • Java Web环保网站开发:SpringBoot+Vue3+MySQL8技术实践
  • AI工具导航站精选与高效使用指南:13个网站深度评测
  • RHCE作业1
  • 【Bug已解决】Bug in accelerator.unwrap_model 解决方案
  • 【第一篇】Xray漏洞扫描软件的使用
  • 2026 年许昌正规的铸铁圆闸门直销厂家选哪家,花千元买的它,竟能扛住八级风浪?别踩这类水利部件的坑! - 企业推荐管【认证】
  • 大型音乐节舞台技术实战:从系统设计到现场故障排查
  • 2026 年现阶段,沁阳正规的实验室净化工程门店怎么联系,做实验室怕脏杂?这玩意儿居然能让数据零干扰还省三成运维成本 - 行业严选官
  • LoNet 808模块实战:GSM/GPRS与GPS/北斗双模定位的物联网开发指南
  • 分时数据深度挖掘:用Python构建日内T+0交易信号系统