LeetCode刷题笔记-283.移动零(move-zeroes)

问题描述

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

说明

  • 必须在原数组上操作,不能拷贝额外的数组。
  • 尽量减少操作次数。


示例 1

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

示例 2

输入: [0,1]
输出: [1,0]



题解

1.双指针解法

算法解析

该算法使用一前一后prepost两个指针,其中pre指针指向已经处理好的序列(全是非零数字)的尾部,即该指针指向的数组位置的元素一定为0;而post指针指向待处理的序列(包括零和非零数字)的头部,即此时post指针指向的元素可能为0非0.而在这两个指针之间的元素一定为0.

因此,post指针的作用就是向后移动寻找到非0元素,然后与pre指针指向的0相交换.当post指向数组尾部(不包括最后一个元素,即越界)时,由于prepost指针之间的元素一直保持为0,而pre之前的元素经过交换后全为非0,所以就完成题解.并且,由于每次交换,都是将pre指针的0post指针的非0数交换,则非0数的相对顺序也不会改变。

参考文章及资料

LeetCode官方题解
LeetCode精选题解 一次遍历部分,快排的思想角度很独特.

复杂度分析
  • 时间复杂度: \(O(N)\), 双指针总计最多遍历整个数组一次,即\(2N\)
  • 空间复杂度: \(O(1)\), 只需要固定的额外空间

代码实现
  • Java版
class Solution {
    public void moveZeroes(int[] nums) {
        int n = nums.length;
        if ((n == 0) || (n == 1)) return;

        int pre = 0, post = 0;
        int temp;
        while (post < n) {
            // 当post指针指向元素不为0时
            // 就交换pre指向的0和post指向的非0
            if (nums[post] != 0) {
                temp = nums[post];
                nums[post] = nums[pre];
                nums[pre] = temp;
                pre++; // 向后指向下一个0
            }
            post++; // 向后寻找非0元素
        }
    }
}
  • Python版本
class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        n = len(nums)
        if n == 0 or n == 1:
            return

        pre = post = 0
        while post < n:
            if nums[post]:
                nums[pre], nums[post] = nums[post], nums[pre]
                pre += 1 # 自增1 找到第一个为0的下标
            post += 1 # 自增1 找到非0数下标


2.移动非0元素解法

算法解析

该方法与双指针解法类似,该方法使用insertPos指针,该指针功能类似于双指针解法的pre指针,但是其指向元素不一定为0,而另一个指针i功能类似于双指针解法的post指针,向后寻找非0数,但是,在找到非0数后并不是将两个指针指向的元素交换,而是将insertPos指针指向的数组位置赋值为i指针指向的非0数,这样就实现了非0数想起移动.因此,insertPos指针和i指针之间的元素就不再和双指针解法里一样全为0了,0非0都存在.

i指针指向的数组尾部(不包括最后一个元素,即越界)时,数组中全部元素都为非0,而insertPos指针指向的元素及其之后的元素在逻辑上就都应该是之前数组中的0,因此只需将它们置0即可.

参考文章及资料

LeetCode精选题解 两次遍历部分
LeetCode精选题解 把非0往前挪部分
LeetCode-Discuss Simple O(N) Java Solution Using Insert Index

复杂度分析
  • 时间复杂度: \(O(N)\), 同双指针解法
  • 空间复杂度: \(O(1)\), 同双指针解法

代码实现
  • Java版(国际站讨论代码)
// Shift non-zero values as far forward as possible
// Fill remaining space with zeros

public void moveZeroes(int[] nums) {
    if (nums == null || nums.length == 0) return;        

    int insertPos = 0;
    for (int num: nums) {
        if (num != 0) nums[insertPos++] = num;
    }        

    while (insertPos < nums.length) {
        nums[insertPos++] = 0;
    }
}
  • C版本
void moveZeroes(int* nums, int numsSize)
{
    int i = 0, insertPos = 0;
    
    for (; i < numsSize; ++i)
    {
        if (nums[i]) nums[insertPos++] = nums[i];
    }

    while (insertPos < numsSize) nums[insertPos++] = 0;
}



解题误区及心得总结

错误代码实例
class Solution {
    public void moveZeroes(int[] nums) {
        int left = 0, right = nums.length-1;
        int temp;
        while (left < right) {
            while (nums[left++] != 0);
            while (nums[right--] == 0);

            temp = nums[right+1];
            nums[right+1] = nums[left-1];
            nums[left-1] = temp;
        }
    }
}
/************************************************/
class Solution {
    public void moveZeroes(int[] nums) {
        int pre = 0, post = 1;
        int temp;
        while (post < nums.length-1) {
            while (nums[pre] != 0) pre++;
            while (nums[post] == 0) post++;

            temp = nums[pre];
            nums[pre] = nums[post];
            nums[post] = temp;
        }
    }
}
误区
  • 在使用双指针时,循环边界条件确定有误,没有正确把握双指针的本质
  • 未正确理清算法步骤情况下,盲目编程
心得总结
  • 注意题干条件即问题特点
  • 严格按照:理清问题-->确定算法步骤-->编写程序,这3个步骤进行解题.防止思维混乱.
posted @ 2021-01-21 01:19  一生至为你  阅读(108)  评论(0)    收藏  举报