LeetCode 热题 HOT 100(双指针) 283.移动零

45 阅读2分钟

题目描述

283. 移动零

简单

提示

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

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

 

示例 1:

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

示例 2:

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

 

提示:

  • 1 <= nums.length <= 104
  • -231 <= nums[i] <= 231 - 1

 

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

思路1 记录出现的0的数量

本题需要保证非0元素的相对次序,因此不能仅仅简单的将0交换到末尾部分。

思路描述:记录之前出现的0的数量X,当遇到一个非0元素时,将这个元素向前移动X个位置(最终位置)。

思路描述图

image.png

从图示中不难看出:zeroNums实际上记录非0元素最终应该放置的位置偏移量,比如上面数组中第一个元素是0,zeroNums=1,这就代表后面的非0元素需要向前移动一个位置以覆盖这个0。当遍历到第三个元素(0)时,zeroNums=2,这就代表后面的非0元素需要向前移动两个位置以覆盖之前出现的2个0导致的空余位置。最后所有非0元素都被移动到数组前半部分,后面的部分都需要置为0。

func moveZeroes(nums []int)  {
    // 从前往后遍历,记录出现的0的数量,非0元素往前移动的步数取决于其值
    zeroNums := 0
    for cur := 0; cur < len(nums); cur++ {
        if nums[cur] == 0 {
            zeroNums++
            continue
        } 
        nums[cur-zeroNums] = nums[cur]
    }
    for i := len(nums)-1; i >= len(nums)-zeroNums; i-- {
        nums[i] = 0
    }
}

思路2:直接将数组当作容器

思路描述:记录存放下一个非0元素的下标(初始为0),遇到非0元素就将其放置到最终位置并更新记录下标,最终将下标最终位置一直到数组末尾的元素全部置为0即可。

func moveZeroes(nums []int)  {
    next := 0
    for i := 0; i < len(nums); i++ {
        if nums[i] != 0 {
            nums[next] = nums[i]
            next++
        }
    }
    clear(nums[next:])
}

思路3:双指针

思路描述:使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。右指针不断向右移动,每次右指针指向非0数,则将左右指针对应的数交换,同时左指针右移。

注意到以下性质:

  1. 左指针左边均非0
  2. 右指针左边指直到左指针处(范围区间)均为0

因此每次交换,都是将左指针的0与右指针的非0数交换,且非0的相对顺序并未改变。

image.png

func moveZeroes(nums []int)  {
    left, right, n := 0, 0, len(nums)
    for right < n {
        if nums[right] != 0 {
            nums[left], nums[right] = nums[right], nums[left]
            left++
        }
        right++
    }
}