LeetCode 数组系列 ~ 移动零

100 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情

概述

数组算是一个并不复杂的数据结构,但就是这个,面试官就能在面试过程中考察出很多东西来。例如经典的排序问题,二分搜索,滑动窗口等等,都是在数组这种最基础等数据结构中处理问题的。

我们做数组类算法问题的时候,常常需要定义一个变量,明确该变量的意义,并不停的维护这个变量,也需要特别注意初始值边界问题。

题目

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

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

示例

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

提示

  • 1 <= nums.length <= 10^4
  • -2^31 <= nums[i] <= 2^31 - 1

代码实现

没接触过双指针的小伙伴,可能就会慢慢遍历,找到每一个为0的元素,然后再去搜索查询出它后面不为0的元素并交换两者的位置。

class Solution {
    public void moveZeroes(int[] nums) {
        for(int i = 0; i < nums.length; ++i){
            // 需要将0移动到数组末尾,所以判断条件为当前元素 == 0
            if(nums[i] == 0){
                // 从当前元素后一位开始
                int j = i + 1;
                // 找到非0元素的位置
                while(j < nums.length && nums[j] == 0){
                    ++j;
                }
                // 防止越界
                if(j != nums.length){
                    // 交换元素
                    nums[i] = nums[i] + nums[j] - (nums[j] = nums[i]);
                }
            }
        }
    }
}

执行结果.png

上面的解法虽然能实现效果,但是可以看到耗时是非常的长的,因为在上面的代码中,有很多重复的步骤,每次找到为0的元素之后都会去做一次后续元素的遍历,时间的开销都耗费在那上面了。我们可以通过双指针的方式,把每次遇到的非0元素都移动到数组前半部分,这样可以避免重复的查询操作。

具体步骤:

  1. 定义leftright两个指针指向数组开头;
  2. 先移动right指针,去数组中查找到非0的元素,将其移动到数组头部,也就是left指针处;
  3. 交换完成之后,将left指针后移一位;
  4. 重复上面两个步骤,一直到遍历完整个数组中的元素位置。
class Solution {
    public void moveZeroes(int[] nums) {
        // 定义两个指针遍历,均先指向数组头部
        int left = 0, right = 0, n = nums.length;
        // 边界处理
        while(right < n){
            // 先移动right指针,找到非0的元素,将其与left指针交换位置,同时将left指针后移一位
            if(nums[right] != 0){
                nums[left] = nums[left] + nums[right] - (nums[right] = nums[left++]);
            }
            ++right;
        }
    }
}

执行结果.png