1、题目解析
将所有0都移动到数组的末尾,并保证数组中非零数的排序是不变的。
这题很简单,但是不能新开数组,不然可以使用新开数组直接移动数据会在逻辑上更好理解和实现。
2、实现代码
2.1 暴力求解
public void moveZeroes(int[] nums) {
for(int i = 0;i<nums.length; i++){
if(nums[i] == 0) {
for(int j=i+1; j<nums.length; j++) {
if(nums[j] != 0) {
nums[i] = nums[j];
nums[j] = 0;
break;
}
}
}
}
}
这里暴力求解的思路很简单,在最外层的循环中对数组中的每个元素进行遍历,遇到非零数就跳过,遇到0就进入下一层循环,找到下一个非零数交换数据。
2.2 计0法
public void moveZeroes(int[] nums) {
int PointA = 0, PointB = 0;
for (; PointA<nums.length; PointA++) {
if(nums[PointA] != 0) {
nums[PointB++] = nums[PointA];
}else {
for(; PointA<nums.length&&nums[PointA]==0 ; PointA++) {}
nums[PointB++] = nums[PointA];
}
}
for (;PointB<nums.length; PointB++) {
nums[PointB] = 0;
}
}
暴力法通过双重循环直接移动数组元素,使程序会消耗大量时间在寻找0和非0数的过程。在遇到一些特殊情况,比如非零数全部集中在数组尾部的情况时两重循环都需要把数组全遍历一遍。因此演化出一种只需要一重循环的新算法。在第一次循环中将所有非零数移动到数组头部,同时遇到0就让计非0指针(PointB)自增。当第一次循环结束时,PointB记录的就是原数组中非零的个数。
接下来的第二重循环中,由于我们在第一次循环中把非零数全部前移,PointB记录的就是最后一个非零数的索引,那么在(PointB,nums.length)区间中的所有数都应该是0。那么直接把后面的元素全部置0即可。
2.3 计0法的引申
这个方法是我在访问leetcode国际站看到的一个小姐姐Olsh的解法,大家有兴趣可以去看看。
从提交结果可以看出这种方法其实在内存消耗和执行用时都相当出色。
public void moveZeroes(int[] nums) {
int zeroes = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 0){
zeroes++;
}else {
if(zeroes != 0){
nums[i - zeroes] = nums[i];
nums[i] = 0;
}
}
}
}
其实这种方法应该算是计0法和暴力法的结合。同样只需要一重遍历,完成了记录0的个数并交换数据两个工作。
这里的含义其实是,遇到0不处理,让计0标记加1,然后在遇到非0数时往前移动,移动的距离就是前面计0的个数,并且把原来位置的数据置0。这种方法执行到后期可以在尾部产生一串0,因为非零数都移动到数组前部。