力扣解题-移动零

力扣解题-移动零

给定一个数组 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. 本题的核心优化方向是:放弃"额外空间换简单逻辑",转而通过指针技巧实现原地操作,减少无效遍历和数据拷贝
相关推荐
不一样的少年_2 分钟前
不用框架,手搓 AI Agent:(一) 先让它跑起来
前端·后端·agent
初学者,亦行者3 分钟前
算法设计与分析3:贪心法 - 求解最短路径问题(TSP)
算法·代理模式
Starmoon_dhw8 分钟前
题解:P16108 「o.OI R-1」基础博弈练习题
c++·算法·图论
用户5268356779015 分钟前
《拒绝当背锅侠!写了个 WebHook 中间件,让局域网硬件在服务挂掉时“喊”出来》
后端
大鱼>21 分钟前
AI+快递分拣:视觉识别+自动分拣+异常检测
人工智能·深度学习·算法·机器学习
想要成为糕糕手23 分钟前
238. 除了自身以外数组的乘积 — 面试向深度解析
javascript·算法·面试
云技纵横26 分钟前
线上报 Deadlock,怎么从日志里找出是哪两条 SQL 打架?
后端
卷无止境27 分钟前
Python数据进阶专题:用声明式验证让数据管道告别"惊喜"
后端·python·数据分析
glartchen35 分钟前
win11下使用 WSL2 部署kali 虚拟机(不踩坑版)
后端
浩瀚地学35 分钟前
【面试算法笔记】0105-数组-螺旋矩阵
java·开发语言·笔记·算法·面试