LeetCode 283. Move Zeros 解题报告

LeetCode 283. Move Zeros 解题报告

题目描述

Given an array nums, write a function to move all 0’s to the end of it while maintaining the relative order of the non-zero elements.


示例

For example, given nums = [0, 1, 0, 3, 12], after calling your function, nums should be [1, 3, 12, 0, 0].


限制条件

  • You must do this in-place without making a copy of the array.
  • Minimize the total number of operations.

解题思路

我的思路:

我尝试了一下,两个循环嵌套是可以暴力破解的。但是我们是有追求的人呐,所以尝试了一下暴力破解之后还是得认真想想更好的办法。
虽然题目说不能拷贝数组,但是我们可以使用额外空间保存数组中的非零元素,然后再放回到正确位置就行。思路其实很直观:
1.遍历一遍数组,记录下0的个数,同时使用一个队列(用vector也可以)存储遇到的非零数字。
2.再次遍历数组,依次将队列中的数字放到相应的位置
3.队列为空后,剩下的位置都设为0。
当然这种方式其实没有优化多少操作,因此下面介绍真正减少了操作数的解法。

参考思路:

题目要求的是把非零的数放到数组前面,0都放到数组后面。按照正常的思维,当然是找到一个0的位置,然后跟后面非零的数交换,然后继续找下一个0的位置……如此重复。这样实际进行的替换操作只有n次,n为第一个0之后的非零数字的数目。
OK,现在我们知道了大概的操作流程,下一步是要用代码实现它。

首先,我们要注意一个前提,按照上述过程,每次0都会出现在有序非零数字序列的最后一个位置的下一位置。
其次,很明显地我们需要知道两个位置信息,一个是有序非零数字序列之后的位置(即第一个0出现的位置),以及该位置之后第一个非零数字的位置。比如:[1, 2, 3, 4, 0, 0, 0, 8],我们要知道数字4后面的0的位置以及8的位置(第一个0之后首个非零数字)。所以我们需要使用两个指针分别指向这两个位置。
两个指针分别为lastNoneZero和cur,初始都是在数组的开头,cur逐个遍历数组的元素,如果是0就跳过(因为cur是用来表示非零数字的位置的),遇到非零数字了,就交换两个指针指向的数字,并且更新lastNoneZero指向下一个位置(因为有序序列里多了一个元素),重复该过程,直到cur到了数组末尾,表明处理完了0后面的所有非零元素。

如果是有在纸上演算一遍上述过程,会发现其实lastNoneZero并不一定是指向非零数字,甚至实际上它大多数情况是指向第一个0。我们可以简单列举一下数组前2个位置出现的各种情况:

L表示lastNoneZero,c表示cur,nums表示数组
1.
 L                 L                    L
[0, 1, 0, ...] -> [0, 1, 0, ...] -> [1, 0, 0, ...]
 c                    c                 c
L指向了第一个0的位置nums[0],c找到第一个非零数字后,交换两者的数字,
并且L指向了下一位置nums[1],该位置现在是第一个0的位置

2.
 L                 L                 L                    L
[0, 0, 1, ...] -> [0, 0, 1, ...] -> [0, 0, 1, ...] -> [1, 0, 0, ...]
 c                    c                    c                 c
过程同上。

3.
 L                    L
[1, 0, 0, ...] -> [1, 0, 0, ...]
 c                 c
L跟c都指向了nums[0],按照上面描述的过程,只要c指向非零数字,就交换两者的数字,
结果nums[0]跟自己交换,然后L指向nums[1],就变成指向了第一个0的位置

4.
 L                    L                 L                    L
[1, 1, 0, ...] -> [1, 1, 0, ...] -> [1, 1, 0, ...] -> [1, 1, 0, ...]
 c                 c                    c                 c
L跟c都指向了nums[0],按照上面描述的过程,只要c指向非零数字,就交换两者的数字,结果nums[0]跟自己交换,
然后L指向下一个位置nums[1]。c遍历数组下一元素,指向nums[1],同样c指向非零数字,交换两者的数字,
结果nums[1]跟自己交换,然后L指向下一个位置nums[2],就是第一个0的位置。

所以我们可以知道无论lastNoneZero开始时指向0或非零,由于cur遇到非零就进行交换的特性,会使得原来的非零数字不会被其它非零数字替换,lastNoneZero也终会变成指向第一个0的位置。

好吧,我自己说得啰嗦,还讲不清,所以建议大家都在纸上对着下面的参考代码,写一下变量的值是怎样变化的,就能有所体会。


代码

我的代码
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int numOfZero = 0;
        queue<int> nonzero;

        for (int i = nums.size() - 1; i >= 0; i--) {
            if (nums[i]) {
                nonzero.push(nums[i]);
            } else {
                numOfZero++;
            }
        }

        for (int i = nums.size() - 1; i >= 0; i--) {
            if (i >= nums.size() - numOfZero) {
                nums[i] = 0;
            } else {
                nums[i] = nonzero.front();
                nonzero.pop();
            }
        }
    }
};
参考代码
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        for (int lastNoneZero = 0, cur = 0; cur < nums.size(); cur++) {
            if (nums[cur] != 0) {
                // Instead, you can use: swap(nums[lastNoneZero++], nums[cur])
                int temp = nums[lastNoneZero];
                nums[lastNoneZero++] = nums[cur];
                nums[cur] = temp;
            }
        }
    }
};

总结

今天的题目是切实地让我们去思考完成一个解法了,有什么办法去优化它,比如这道题,暴力破解完全可以,但是实际中,我们却不会使用暴力破解。因此解决了题目不是最终目的,知道有什么优化方式甚至是更好的解法才是刷题的意义所在。LeetCode官网这道题有个官方的解法说明,里面提了一下怎样一步步优化解法,这个思维过程还是挺不错的。
踏平今天的坑,明天继续,加油!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值