[双指针] 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)。