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

【LeetCode 热题 100】No.283—— 移动零

大家好!今天继续更新 LeetCode 热题 100 系列,第四题我们来学习简单题 “移动零”。这道题看似简单,但涉及到 “原地操作” 和 “最少操作次数” 的优化思想,是面试中考察代码细节的常见题目。接下来,我们从题目分析、思路推导到 Java 代码实现,一步步掌握这道题的最优解法。

一、题目描述

首先明确题目要求(基于 LeetCode 官方原题):

  • 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
  • 请注意,必须在不复制数组的情况下原地对数组进行操作。
  • 元素的相对顺序必须保持一致。
  • 尽量减少操作次数。

示例

示例 1:输入:nums = [0,1,0,3,12]输出:[1,3,12,0,0]

示例 2:输入:nums = [0]输出:[0]

提示

  • 1 <= nums.length <= 10⁴
  • -2³¹ <= nums[i] <= 2³¹ - 1

二、解题思路分析

这道题的核心要求是 “原地操作” 和 “保持非零元素顺序”,同时要 “减少操作次数”。我们可以从基础思路入手,逐步优化到最优解法。

1. 基础思路:两次遍历(符合所有要求)

核心逻辑

第一次遍历:把所有非零元素 “前移”,用一个指针记录非零元素的位置;第二次遍历:将剩余位置填充为 0。

步骤拆解
  • 定义指针nonZeroIndex,初始值 0,用于记录下一个非零元素应该存放的位置。
  • 第一次遍历数组:
    • 若当前元素nums[i]非零,则将其放到nums[nonZeroIndex]的位置,然后nonZeroIndex加 1。
    • 若当前元素是 0,直接跳过。
  • 第一次遍历结束后,nonZeroIndex之前的位置已全部填充非零元素,且保持原顺序。
  • 第二次遍历数组:从nonZeroIndex开始到数组末尾,将所有位置填充为 0。
优势
  • 原地操作,无需额外空间(除了几个指针变量)。
  • 非零元素只移动一次,操作次数少。
  • 时间复杂度 O (n),两次遍历总次数为 2n,属于线性时间。

2. 常见误区:交换法的注意事项

另一种思路是 “遍历数组时,遇到 0 就与后面的非零元素交换”,但这种方法可能导致非零元素被多次移动(例如[0,0,0,1]需要交换 3 次),操作次数更多,不符合 “尽量减少操作次数” 的要求,因此不推荐。

三、Java 代码实现(两次遍历法)

java

运行

public class Solution {public void moveZeroes(int[] nums) {if (nums == null || nums.length == 0) {return; // 边界处理:数组为空或长度为0,直接返回}int nonZeroIndex = 0; // 记录下一个非零元素的存放位置// 第一次遍历:将所有非零元素前移for (int i = 0; i < nums.length; i++) {if (nums[i] != 0) {nums[nonZeroIndex] = nums[i];nonZeroIndex++;}}// 第二次遍历:将剩余位置填充为0for (int i = nonZeroIndex; i < nums.length; i++) {nums[i] = 0;}}
}

代码解析

  1. 边界处理:先判断数组是否为空或长度为 0,直接返回,避免无效操作。
  2. 非零元素前移:通过nonZeroIndex指针跟踪非零元素的位置,遍历过程中只处理非零元素,确保它们按原顺序排列在数组前部。
  3. 填充零:第一次遍历结束后,nonZeroIndex之后的位置原本要么是 0,要么是被前移元素覆盖的旧值,因此直接填充 0 即可完成所有零的移动。

四、优化:一次遍历(减少写操作)

上述两次遍历法已经很好,但可以进一步优化:在一次遍历中同时完成 “前移非零元素” 和 “记录零的位置”,减少对数组的写操作(尤其当零元素较少时)。

public class Solution {public void moveZeroes(int[] nums) {if (nums == null || nums.length == 0) {return;}int zeroIndex = 0; // 记录第一个零元素的位置for (int i = 0; i < nums.length; i++) {if (nums[i] != 0) {// 若当前元素非零,且与零元素位置不同,则交换(避免自我交换)if (i != zeroIndex) {nums[zeroIndex] = nums[i];nums[i] = 0;}zeroIndex++; // 无论是否交换,零元素位置都后移}}}
}

优化点解析

  • zeroIndex跟踪第一个零元素的位置,遍历过程中:
    • 若遇到非零元素,且当前位置i大于zeroIndex(说明存在零元素),则将非零元素与zeroIndex位置的零交换,同时zeroIndex后移。
    • i == zeroIndex(说明还未遇到零元素),则直接zeroIndex后移,不进行交换(减少无效写操作)。
  • 优势:当数组中零元素较少时,写操作次数更少(例如[1,2,3,0,4]只需 1 次交换)。

五、测试案例验证

测试案例 1:基础示例

输入:[0,1,0,3,12]两次遍历法处理:

  • 第一次遍历后:[1,3,12,3,12]nonZeroIndex=3)。
  • 第二次遍历后:[1,3,12,0,0],正确。

为了更直观的感受到执行过程,我将对执行过程逐步拆解

初始状态

  • 数组:[0, 1, 0, 3, 12]
  • zeroIndex = 0(初始指向第一个元素)
  • i从 0 开始遍历
第 1 步:i=0
  • 当前元素nums[0] = 0(是零)
  • 不进入if (nums[i] != 0)逻辑
  • zeroIndex保持 0 不变
  • 数组无变化:[0, 1, 0, 3, 12]
第 2 步:i=1
  • 当前元素nums[1] = 1(非零)
  • 判断i != zeroIndex1 != 0 → 成立
    • 执行交换:nums[zeroIndex] = nums[i] → nums[0] = 1nums[i] = 0 → nums[1] = 0
  • 执行zeroIndex++ → zeroIndex = 1
  • 数组变为:[1, 0, 0, 3, 12]
第 3 步:i=2
  • 当前元素nums[2] = 0(是零)
  • 不进入if (nums[i] != 0)逻辑
  • zeroIndex保持 1 不变
  • 数组无变化:[1, 0, 0, 3, 12]
第 4 步:i=3
  • 当前元素nums[3] = 3(非零)
  • 判断i != zeroIndex3 != 1 → 成立
    • 执行交换:nums[1] = 3nums[3] = 0
  • 执行zeroIndex++ → zeroIndex = 2
  • 数组变为:[1, 3, 0, 0, 12]
第 5 步:i=4
  • 当前元素nums[4] = 12(非零)
  • 判断i != zeroIndex4 != 2 → 成立
    • 执行交换:nums[2] = 12nums[4] = 0
  • 执行zeroIndex++ → zeroIndex = 3
  • 数组变为:[1, 3, 12, 0, 0]

测试案例 2:全为非零元素

输入:[0] 处理:第一次遍历后 nonZeroIndex=0,第二次遍历将从索引 0 到 0 的位置填充为 0,数组保持不变,正确

六、复杂度分析

  • 时间复杂度:O (n)。无论是两次遍历还是一次遍历,都只对数组进行线性扫描,总操作次数为 n 级。
  • 空间复杂度:O (1)。仅使用常数个额外变量,符合原地操作要求。

七、总结

“移动零” 的解题关键是在原地操作的基础上,减少不必要的元素移动,核心要点如下:

  1. 两次遍历法是最直观的解法,逻辑清晰,操作次数少,适合面试快速实现。
  2. 一次遍历的优化版通过交换进一步减少写操作,在零元素较少时更高效,但逻辑稍复杂。
  3. 避免多次移动同一非零元素,这是减少操作次数的核心原则。

这道题虽然简单,但能体现对代码细节的把控能力,尤其是 “最少操作次数” 的优化思想在实际开发中很有价值。下一篇我们继续讲解 LeetCode 热题 100 的下一道题。

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

相关文章:

  • 旅游景点网站策划书香河住房与建设局网站
  • jvm双亲委派的含义
  • 【linux内核驱动day06-I2C】
  • Photoshop - Photoshop 工具栏(10)透视裁剪工具
  • 一种基于 RK3568+AI 的国产化充电桩安全智能交互终端的设计与实现,终端支持各种复杂的交互功能和实时数据处理需求
  • SSM动漫衍生品交易平台z25so(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • Canny边缘检测算法详解
  • 不止是 Python:聊聊 Node.js/Puppeteer 在爬虫领域的应用
  • MCP协议深度解析:AI时代的通用连接器
  • 首钢建设网站网站源码程序修改
  • Doris专题11- 数据导入概览
  • 厦门优化网站排名合肥网站改版
  • 详解Spring Security+OAuth2.0 和 sa-token
  • 临沂企业建站程序德国网站的后缀名
  • Day14_内核编译安装
  • 全面SEO优化指南:网站运营与前端开发的协同策略
  • 网站整站优化公司赣州平面设计公司
  • DAY03:【DL 第一弹】神经网络
  • 2018年下半年试题四:论NoSQL数据库技术及其应用
  • 如何检查网站死链网站建设技术咨询协议
  • 【MATLAB技巧】contour|等高线图绘制,使用示例和使用技巧
  • matlab计算算法的运行时间
  • 有人情味的网站北京大数据公司排行
  • 时间箭头 量子信息的不可逆扩散
  • iPhone 17 Pro Max 的影像升级全解:从长焦、前置聊到 ProRes RAW
  • 阿里巴巴国际站费用淘宝装修做代码的网站
  • 解决comet等待网络连接的问题
  • 【开题答辩全过程】以 爱宠宠物商店管理系统的设计与实现为例,包含答辩的问题和答案
  • Spring Boot Actuator+Micrometer:高并发下 JVM 监控体系的轻量化实践
  • 建设网站 深圳石家庄企业网站建设