LeetCode283 移动零

题目

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

Note that you must do this in-place without making a copy of the array.

思路

使用双指针,pz指向第一个0开始的位置,pn指向第一个非0开始的位置,每一次操作交换两指针指向的数值。用该思路模拟一遍输入示例:

0 1 0 3 12
pz=0, pn=1
1 0 0 3 12
pz=1, pn=3
1 3 0 0 12
pz=2, pn=4
1 3 12 0 0 

因此大致可以判断使用一个while循环来做,每一次循环先找到第一个0的位置,再找到第一个非0的位置。
循环的结束条件 理所当然地可以想到,当两个指针都到数组末尾位置n-1的时候,应该退出循环。虽然这种做法称不上优雅,但是至少可以作为暂时的思路。

int pz = 0, pn = 0;
while(true){
	while(nums[pz] && pz < len - 1) pz ++; //找第一个0的位置
	while(!nums[pn] && pn < len - 1) pn ++; //找第一个非0的位置
	if(pz == len - 1) break; //0指向末尾,无需进行任何交换操作,直接结束
	/*执行交换操作*/
	if(pn == len - 1) break; //非0指向末尾,并且已经执行过交换操作,结束
}

预处理 考虑到以下一种情况:

1 0 1

当我们使用上述思路进行操作的时候,会意外地发现,最后输出的结果会是:

0 1 1

这是因为在我们的循环当中,总是第一个0和第一个非0进行交换,而无论谁的顺序在前都会执行该操作。因此,想到在循环之前先行找到第一个0的位置,而后直接将pn(非0指针)拨到0的位置上,从而确保非0指针一定是在0指针之后的。
时间复杂度 该思路的时间复杂度为0-2n. 当数组全部为0时,无需任何操作;当数组第一位为0,后续都非0时,pz从0遍历到n,pn从0遍历到n,总共需要执行2n次操作。

C++代码

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int pz = 0;
        int len = nums.size();
        while(nums[pz] && pz < len - 1) pz++;
        int pn = pz;
        while(true){
            while(nums[pz] && pz < len - 1) pz++;
            while(!nums[pn] && pn < len - 1) pn++;
            if(pz == len - 1) break;
            int temp = nums[pz];
            nums[pz] = nums[pn];
            nums[pn] = temp;
            if(pn == len - 1) break;
        }
    }
};

官方题解

使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。
left: 左指针左边均为非零数 -> 指向第一个0
right: 右指针左边直到左指针处均为零 -> 指向第一个非0

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int n = nums.size(), left = 0, right = 0;
        while (right < n) {
            if (nums[right]) {
                swap(nums[left], nums[right]);
                left++;
            }
            right++;
        }
    }
};

反思

官方题解提供的一种思路,即将待处理数组一分为二,处理好的序列&待处理序列。虽然left也是指向第一个0的位置,也就是pz,但是其是作为处理好的序列的末尾来看待的。也就是说,处理好的序列包括[num0, num1, num2, …, 0]. 待处理序列包括[num_r, …]. 该思路的代码写的非常优雅,while的结束条件为右指针不超出数组,处理1 0 1的时候,它直接解决了r在l左侧的问题。比while里面两个while找位置要优雅很多很多,非常值得学习。
我的思路仅仅是看点对点进行交换,因此需要考虑很多种情况,造成break的条件不清晰,结束条件比较的长度是多少、在哪个位置进行break等等都需要考虑。同时,我的思路当中的处理好的序列其实是不包含末尾0的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ayu阿予

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值