一、题目解析
本题选自:. - 力扣(LeetCode)
本题要求将一个给定数组nums在保持非零元素相对顺序不变的情况下,将零移动到数组的末尾,如示例1中 nums = 【0,1,0,3,12】,在【1,3,12】的排序顺序不变的情况下,将nums中间的零挪到末尾也就是变为 nums = 【1,3,12,0,0】
二、讲解算法原理
这道题其实可以归到一道类型题里,这类题的名字叫做数组划分,或者可以叫为数组分块。
这类题的特点为:给一个数组,并给我们制定了一个标准,让我们在这个规则下将这个数组分为若干个区间。
在我们移动零这道题里面可以看成给我们一个标准,也就是将数组中非零元素部分移到左边,零元素移到右边。所以我们可以以此分为两个区间,其中非零元素部分为左区间,零元素部分为右区间。
而这类题可以用到一个非常经典的算法,也就是双指针算法。
在这道题中,由于数组的下标可以索引到数组中的元素,因此没有必要定义两个指针常量而是可以直接用数组的下标来进行数组的操作。
具体做法如下:
定义两个指针:dest(最终的)、cur(当前的)。
dest的作用为:已处理的区间内,非零元素的最后一个位置。
cur的作用为:从左到右扫描、遍历数组。
在一个数组(0 ~ n-1)中,将两个指针来代表数组中的下标我们可以将该数组分为二个部分:
【0 , cur - 1】(处理过区间)、【cur ,n - 1】(待处理区间)。
而处理过区间可以细分为两个区间:
【0 , dest】(非零元素区间)、【dest + 1 ,cur - 1】(零元素区间)。
其中 dest 在左,cur 在右。当 cur 指针从左到右扫描完这个数组后,可以判定为区间已划分完毕。
拿示例一为例:在 nums = 【0,1,0,3,12】这个数组中 ,由于 cur 指针的作用为从左到右扫描这个数组,所以应该将 cur 的开始位置放在 nums 中的第一个元素0(下标为0)上,也就是 cur = 0。而由于在扫描这个数组前,我们并不知道已处理的区间中非零元素的最后一个位置,所以我们可以将 dest 指针放在数组开始之前,也就是 dest = -1 。
由于例一中,nums 数组的第一个元素是0,所以 dest 不变,cur++。而当 cur 指向nums 中第二个元素,也就是1时,由于 dest 为非零元素的最后一个位置,所以dest + 1 就为零元素,那么将目前 cur 指向的元素1 与 dest + 1 指向的元素也就是0调换位置,同时 cur++,dest++,最后nums 数组变为了【1,0,0,3,12】,cur = 2 指向该数组第三个元素0,dest = 0 指向该数组第一个元素1。
所以我们可以得到判断语句:如果 nums【cur】=0 ,cur++。如果 nums【cur】!=0 ,
swap( nums【dest + 1】,nums【cur】),cur++,dest++。
最后本题代码如下:
void moveZeroes(int* nums, int numsSize) {
int dest = -1;
int cur = 0;
while(cur < numsSize){
if(nums[cur]==0){
++cur;
}else{
int tmp;
tmp = nums[cur];
nums[cur] = nums[dest+1];
nums[dest+1] = tmp;
++cur;
++dest;
}
}
}