[双指针] 283. 移动零(双指针+两次遍历 → 一次遍历)

283. 移动零

题目链接:https://leetcode-cn.com/problems/move-zeroes/


分类:

  • 双指针:
    • 两次遍历:p1寻找非零元素,p2拿p1找到的非零元素覆盖零元素(思路1);
    • 一次遍历:快排思想,零元素作为枢轴,非零元素放到左边,零元素放到右边(思路2)。

在这里插入图片描述

题目分析

题目要求:在原数组上操作,不能使用额外的数组,且要求非零元素的相对顺序保持不变,操作次数尽可能少。

思路1:双指针 + 两次遍历(O(2N))

我们在原数组上做处理,设置两个指针p和np,p用于遍历原数组,寻找非零元素,np用于将p找到的每个非零元素按顺序覆盖数组里的零元素。

其实相当于两个指针分别工作在两个数组上,p工作在原数组,np工作在空数组,每在原数组上找到一个非零元素,就填充到空数组上,现在只是把这两个指针都放到原数组上处理了。

整体工作流程

初始时np=0,p=0,判断nums[p]是否等于0:

  • 如果nums[p]!=0,则令nums[np]=nums[p],np++,p++;
  • 如果nums[p]==0,则np不动,p++,直到遇到nums[p]!=0,重复上面的步骤。
例如:[0,1,0,3,12]

初始时np=0,p=0,因为nums[np]=nums[p]=0,所以p++,np不动;
np=0,p=1,因为nums[p]!=0,所以令nums[np]=nums[p],得到[1,1,0,3,12],np++,p++;
np=1,p=2,因为nums[p]==0,所以p++;
np=1,p=3,因为nums[p]==3,所以令nums[np]=nums[p],得到[1,3,0,3,12],np++,p++;
np=2,p=4,因为nums[p]==12,所以令nums[np]=nums[p],得到[1,3,12,3,12],np++,p++;

此时p=5到达数组末尾,就将nums[np~n-1]的所有元素都置0,得到[1,3,12,0,0],实现了将所有0移动到数组末尾的效果。

实现代码:

class Solution {
    public void moveZeroes(int[] nums) {
        int np = 0, p = 0;
        //将所有非零元素前移
        while(p < nums.length){
            if(nums[p] != 0){
                nums[np++] = nums[p++];
            }
            else p++;
        }
        //收尾工作:将nums[np...n-1]都置0
        while(np < nums.length){
            nums[np++] = 0;
        }
    }
}
  • 时间复杂度:np,p两个指针都完整地遍历一遍数组,所以实际需要O(2N)。
  • 空间复杂度:O(1).

思路2:双指针 + 一次遍历(O(N))

一次遍历学习了快速排序的思想:选择一个枢轴,将小于枢轴的放到它的左边,大于枢轴的放到它的右边;

这里用的是快排的简化思想:选择一个0元素,将不等于0的元素放到它的左边,等于0的元素就放到它的右边。

算法流程:

设置两个指针np,p,两个指针同步右移,直到找到第一个零元素nums[np]作为枢轴,p继续向后遍历数组,进入循环:

  • 如果nums[p] != 0,就拿它和nums[np]交换,np+1,p+1;
    • np+1后,相当于舍弃原来的枢轴,所以需要重新寻找下一个零元素作枢轴;
  • 如果nums[p] == 0,则p++。

当p到达数组末尾时,退出循环。

实现时要注意:

1、两次寻找0元素作为枢轴的操作有所不同,第一个枢轴需要np,p同步移动,第二个枢轴只需要移动np,p用于寻找非零元素。

2、数组下标的越界判断。

实现代码:

class Solution {
    public void moveZeroes(int[] nums) {
        int np = 0, p = 0;
        //寻找第一个0元素作为枢轴
        while(np < nums.length && nums[np] != 0){
            np++;
            p++;
        }
        while(p < nums.length){
            //寻找下一个枢轴(找到第一个枢轴会跳过这一步)
            while(nums[np] != 0){
                np++;
            }
            //此时nums[np]指向0元素作为枢轴
            //p寻找非零元素和枢轴交换
            while(nums[p] == 0){
                p++;
                //下标越界判断
                if(p >= nums.length) return;
            }

            //元素交换
            nums[np] = nums[p];
            nums[p] = 0;
            //完成交换后两指针同步加1
            np++;
            p++;
        }
    }
}
  • 时间复杂度:思路2同样设置了双指针遍历数组,但只要p指针到达数组末尾时就退出循环,所以实际时间复杂度为O(N)。
  • 空间复杂度:O(1)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值