T283题目描述
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
解法一
思路
- 定义两个指针
a
,b
, a指针
用于遍历数组查找非0元素
,b指针
在后面用于接收a指针
找到的非0元素
- 当
a指针
到达数组末尾时,非0元素
已经全部完成重新覆盖数组 - 将剩下的元素用0覆盖
代码
//方法体
public void moveZeroes(int[] nums) {
int j=0;
for(int i=0;i<nums.length;i++){
if(nums[i] != 0){
nums[j] = nums[i];
j++;
}
}
//注意:跳出循环时,j已经在最后一个非0元素下标的基础上,加了一个1,所以,直接从j开始用0覆盖
for(int i=j;i<nums.length;i++){
nums[i] = 0;
}
for (int num : nums) {
System.out.println(num);
}
}
解法二
思路
- 定义两个指针
a
,b
- 模拟情景:同学A和同学B肩并肩走在路上,突然他们发现马路上坑,由于同学A有轻功,不受“坑”的影响,同学B年年体育不达标,于是两人商量着,由同学A继续向前走寻找井盖,而同学B留下来等待同学A给他送来井盖,这样他就能继续往后走了,等到同学A找到了井盖,就将井盖抛给同学B,让他前进,若路上没有看见未盖上的井口,同学A不想等同学B,只想自己着在同学B遇到“坑”的时候再给它送个井盖,然后快点到终点,剩下的事它就不管了…等到同学A已经到达这段危险路段的终点时,同学B还在徘徊,因为…去终点的路上全是坑,已经没有井盖可以填补了…
- 上述例子中,未盖上的井口为
0
,盖上的井口为非0
。
代码
public void moveZeroes(int[] nums) {
int j=0;
for(int i=0;i<nums.length;i++){
/*
下列注释掉的代码为分步骤执行的代码
*/
//没碰到“坑”就都继续往后走
// if(nums[i]!=0 && nums[j]!=0){
// j++;
// }
//两个人都碰到了坑,那也没办法,前面那个坑还没填好,那么同学B只能继续等待,同学A则继续往后找井盖
// else if(nums[i]==0 && nums[j]==0){
// continue;
// }
//同学A找到了井盖,并给同学B补“坑”,补完之后,同学B就可以继续往后走了,同学A有轻功,不受“坑”的影响,同时,它也不想等同学B,只想自己着在同学B遇到“坑”的时候再给它送个井盖,然后快点到终点,剩下的事它就不管了
// else if (nums[i]!=0 && nums[j]==0){
// int temp = nums[i];
// nums[i] = 0;
// nums[j] = temp;
// j++;
// }
//同学A遇到了“坑”,同学B还没遇到,同学A不吭声,继续向前走,同学B不知道前面有个“坑”,他也会继续向前走,因为他迟早会遇到
// else if(nums[i]==0&&nums[j]!=0){
// j++;
// }
//简写代码
if(nums[i]!=0){
if(nums[j]==0){
int temp = nums[i];
nums[i] = 0;
nums[j] = temp;
}
j++;
}
}
for (int num : nums) {
System.out.println(num);
}
}
解题收获
错误点
- 一开始我是想将各个0的下标记录下来,之后利用这些下标对数组进行分段,每一段分别向前移动不同的位数(前面有几个0就移动几位),发现这样不现实,因为在移动的过程中数组是变化的,这样之前记录的下标就不稳定了,导致过程很复杂,最后也没有将思路实现,这很可惜。
- 后来我的想法是碰到一个0就往前移动,移动后指针指向的元素就不是0了,若是继续向后遍历,势必会出错,也意味着一个指针做不到,使用for循环会很麻烦。
疑惑点
解法二提到借用了快速排序
的思想,但我很懵…,需要再将快速排序的算法看一遍
原大佬的解释如下:
这里参考了快速排序的思想,快速排序首先要确定一个待分割的元素做中间点x,然后把所有小于等于x的元素放到x的左边,大于x的元素放到其右边。
这里我们可以用0当做这个中间点,把不等于0(注意题目没说不能有负数)的放到中间点的左边,等于0的放到其右边。
这的中间点就是0本身,所以实现起来比快速排序简单很多,我们使用两个指针i和j,只要nums[i]!=0,我们就交换nums[i]和nums[j]
等我将快排理解完后再回来看这句话~