【LC75】No283. 移动零

题目描述

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

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

提示:

  • 1 <= nums.length <= 104

  • -231 <= nums[i] <= 231 - 1

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

示例

示例 1:

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

示例 2:

输入: nums = [0]
输出: [0]

链接

https://leetcode.cn/problems/move-zeroes/description/?envType=study-plan-v2&envId=leetcode-75

思路

依旧是很好理解的题目,没有背景设置,所以中译中环节省略。

注意题目中的一点要求,在不复制数组的情况下对原数组进行操作。

解法一:非零前移,末尾补0

数组中非 0 和 0 交替出现,我们最先可以想到最简单的办法是,将每个 0 后的数字前移,然后在结尾补 0 填充完整个数组。

代码

class Solution {
    public void moveZeroes(int[] nums) {
        if (nums.length == 1)
            return;
        int j = 0;
        int len = nums.length;
        for (int i = 0; i < len; i++) {
            if (nums[i] != 0) {
                nums[j++] = nums[i];
            }
        }
        for (int i = j; i < len; i++) {
            nums[i] = 0;
        }
    }
}
  • 时间复杂度:O(n)

  • 空间复杂度:O(1)

解法二:双指针

解法一使用了两次遍历,那么我们可以优化在一次遍历中完成吗?

解题需要我们分别找到 0 和 后面的非 0,然后进行交换,对于这个思路,很容易想到快慢指针。

定义一个慢指针 left,让它永远指向数组从左开始的第一个 0;定义一个快指针 right,让它作为循环遍历的标识。

当快指针 right 找到第一个非 0 时,和 left 指向的 第一个 0 进行交换,之后让 left++ 指向下一个元素。

此方法的巧妙在于:

  • 慢指针 left 只有在交换后才向右移动。所以 left 的左侧,全部是已经处理好的数据,其中必然不包含 0 。

  • 快指针 right 只有在指向非 0 时才交换,即遇到 0 就直接 right++ 跳过不进行操作。所以 left 和 right 之间,全部都是 0。

  • 快指针 right 遇到 0 元素就跳过,当数组开头是连续非 0 元素时,left 和 right 会一起向右移动。

代码

class Solution {
    public void moveZeroes(int[] nums) {
        if (nums.length == 1)
            return;
        int left = 0, right = 0;
        int len = nums.length;
        while (right < len) {
            if (nums[right] != 0) {
                int tmp = nums[left];
                nums[left] = nums[right];
                nums[right] = tmp;
                left++;
            }
            right++;
        }
    }
}
  • 时间复杂度:O(n)

  • 空间复杂度:O(1)


【LC75】No283. 移动零
https://tiamo495.com//archives/lc75-no283.-yi-dong-ling
作者
tiamo495
发布于
2025年03月24日
许可协议