力扣解题-移动零

力扣解题-移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

示例 1:

输入: nums = 0,1,0,3,12

输出: 1,3,12,0,0

示例 2:

输入: nums = 0

输出: 0

提示:

1 <= nums.length <= 104

-231 <= numsi <= 231 - 1

进阶:你能尽量减少完成的操作次数吗?

Related Topics

数组

双指针


第一次解答

解题思路

核心方法:分类存储非零/零元素 + 回填原数组,通过两个列表分别存放非零元素和零元素,再按顺序将元素回填到原数组,逻辑简单直观,但存在额外内存开销和冗余操作,性能较差。

具体步骤:

  1. 初始化两个列表:numList用于存储数组中的所有非零元素,zeroList用于存储所有零元素,通过列表分类实现"分离非零和零元素"的核心目标。
  2. 遍历原数组nums,将每个元素按"是否为0"分类:非零元素加入numList,零元素加入zeroList,完成元素分类。
  3. 先遍历numList,将其中的非零元素按原有顺序依次回填到原数组nums的前半部分(下标从0开始),保证非零元素的相对顺序不变。
  4. 再遍历zeroList,将所有零元素回填到原数组numsnumList之后的位置,完成"零元素移到末尾"的要求。
  5. 最终原数组nums即为处理后的结果。

性能劣势说明

该解法虽然实现了核心功能,但不符合题目"尽量减少操作次数"的进阶要求,且内存和耗时表现差:

  1. 额外内存开销:创建两个列表存储元素,增加了O(n)的内存消耗,导致内存击败率仅5.09%;
  2. 冗余操作次数:需要三次遍历(遍历原数组分类+遍历非零列表回填+遍历零列表回填),且涉及列表的增删/访问操作,整体操作次数多,耗时击败率仅9.66%;
  3. 未充分利用"原地操作"的优化空间,本质是通过额外空间换逻辑简单,而非高效的原地调整。

执行耗时:6 ms,击败了9.66% 的Java用户

内存消耗:47.6 MB,击败了5.09% 的Java用户

java 复制代码
public void moveZeroes(int[] nums) {
        List<Integer> numList=new ArrayList<>();
        List<Integer> zeroList=new ArrayList<>();
        for (int num : nums) {
            if (num==0){
                zeroList.add(num);
            }else {
                numList.add(num);
            }
        }
        for(int i=0;i<numList.size();i++){
            nums[i]=numList.get(i);
        }
        for(int i=0;i<zeroList.size();i++){
            nums[numList.size()+i]=zeroList.get(i);
        }
    }

第二次解答

解题思路

核心方法:单指针标记非零元素位置 + 原地归位非零元素 + 末尾统一补零,完全基于原数组原地操作,消除额外内存开销,大幅减少操作次数,是更贴合题目进阶要求的高效解法。

具体步骤:

  1. 初始化两个关键变量:
    • zeroIndex:作为非零元素的目标占位指针,初始值为0,标记下一个非零元素应存放的位置;
    • zeroNum:统计数组中零元素的个数,用于后续补零操作。
  2. 遍历原数组nums的每个元素,核心逻辑是"归位非零元素":
    • 若当前元素nums[i] != 0:将该非零元素赋值到zeroIndex指向的位置(相当于把非零元素按顺序"挤"到数组前面),然后zeroIndex向后移动一位,为下一个非零元素预留位置;
    • 若当前元素nums[i] == 0:仅将zeroNum加1,不做其他操作(零元素暂时不处理,留到最后统一补零)。
  3. 遍历完成后,所有非零元素已按原有顺序排列在数组前nums.length - zeroNum个位置;此时从下标nums.length - zeroNum开始,到数组末尾的位置,统一赋值为0,完成"零元素移到末尾"的要求。
  4. 全程无额外集合创建,仅在原数组上完成操作,且仅需两次短遍历(一次归位非零元素,一次补零)。

核心优化点说明

  1. 原地操作,消除内存开销:不再创建额外列表,所有操作基于原数组,内存消耗大幅降低;
  2. 减少操作次数:仅一次遍历完成所有非零元素的归位,补零阶段仅遍历零元素个数的位置,相比第一次解答的三次遍历,操作次数减少约1/3;
  3. 无冗余数据拷贝:通过指针直接赋值非零元素,避免列表增删、访问的额外开销,是耗时从6ms降至1ms的核心原因;
  4. 完全满足题目"保持非零元素相对顺序"和"原地操作"的核心要求,同时贴合"尽量减少操作次数"的进阶目标。

执行耗时:1 ms,击败了99.98% 的Java用户

内存消耗:47.3 MB,击败了5.09% 的Java用户

java 复制代码
public void moveZeroes(int[] nums) {
        //假定位置0所在的位置
        int zeroIndex=0;
        int zeroNum=0;
        for(int i=0;i<nums.length;i++){
            if(nums[i]!=0){
                nums[zeroIndex]=nums[i];
                zeroIndex++;
            }else {
                zeroNum++;
            }
       }
        for(int i=nums.length-zeroNum;i<nums.length;i++){
            nums[i]=0;
        }
    }

总结

  1. 第一次解答通过"列表分类+回填"实现功能,逻辑简单但存在额外内存开销和冗余遍历,性能较差;
  2. 第二次解答采用"单指针归位非零元素+末尾补零"的原地操作思路,消除额外空间、减少操作次数,是高效且贴合题目要求的最优解;
  3. 本题的核心优化方向是:放弃"额外空间换简单逻辑",转而通过指针技巧实现原地操作,减少无效遍历和数据拷贝
相关推荐
xieliyu.12 小时前
Java算法精讲:双指针(三)
java·开发语言·算法
星辰徐哥12 小时前
Spring Boot 微服务架构设计与实现
spring boot·后端·微服务
星辰徐哥12 小时前
Spring Boot 数据导入导出与报表生成
spring boot·后端·ui
明夜之约12 小时前
Spring Boot 自动装配源码
java·spring boot·后端
Leaton Lee12 小时前
Spring Boot分层架构详解:从Controller到Service再到Mapper的完整流程
java·spring boot·后端·架构
Micro麦可乐12 小时前
Spring Boot 实战:从零设计一个短链系统(含完整代码与数据库设计)
数据库·spring boot·后端·哈希算法·雪花算法·短链系统
Jinkxs12 小时前
Resilience4j- 与 Spring Boot 快速集成:自动配置与基础注解使用
java·spring boot·后端
毕设源码_郑学姐12 小时前
计算机毕业设计springboot网络相册设计与实现 基于Spring Boot框架的在线相册管理系统开发与应用 Spring Boot驱动的网络影集设计与实践
spring boot·后端·课程设计
辣机小司12 小时前
【踩坑记录:Spring Boot 配置文件读取值不一致?警惕 YAML 的“八进制陷阱”与 SnakeYAML 版本之谜】
java·spring boot·后端·yaml·踩坑记录
一条小锦吕*12 小时前
基于Spring Boot + 数据可视化 + 协同过滤算法的推荐系统设计与实现(源码+论文+部署全讲解)
spring boot·算法·信息可视化