目录
[1. 题目解析](#1. 题目解析)
[2. 算法讲解](#2. 算法讲解)
[3. 编写代码](#3. 编写代码)
1. 题目解析
移动0的题型是为数组划分,或者数组分块。
这类题的特点是给我们一个数组,再制定一个规则,让我们在这个规则下,把数组划分成不同的区间:
这个题的标准是:
在原数组的基础上,把所有非0元素划分到数组的左边,所有0元素分配到数组的右边 。
这样,所有数组元素就被划分成两个区间:
解决这个题目,我们用一种非常经典的算法,就是双指针算法(利用数组下标充当指针)
2. 算法讲解
双指针算法:我们定义两个指针 dest ,cur
这两个指针从左往右遍历数组的时候,会把整个数组划分为三个区间:
我们先不管这些区间,先来介绍这两个指针的作用:
cur :从左往右扫描数组,遍历数组
dest:已处理的区间内,非零元素的最后一个位置
这时候,cur会把整个数组划分成两个区间,**左边为已经处理的区间,右边为未处理的区间,**怎么处理的我们先不管;
dest指针起到分割线的作用,将cur左边已经处理过的区间又细分成两个小区间。
这里又有一个小问题:为什么第二个区间是到cur-1呢?
这又回到cur指针本身的含义上,cur这个指针用于从左往右扫描数组,而这三个区间分别代表不同的含义:
当cur走到n下标,就说明这个数组已经全部扫描完成。这时候,数组就被 dest 划分成两部分;也就是刚刚三个区间,只剩下前面两个区间,其中,左边部分是非0元素,右边部分是0元素。
那么接下来,我们要做的就是,让这两个指针从左往右扫码数组的过程中,保持两个指针划分的三个区间的性质。
以这个图的数组为例子:
cur刚开始初始化为这个数组的0下标位置;
对于 dest 指针,它表示非0元素的最后一个位置,刚开始扫描数组的时候,并没有非0元素;所以 刚开始让dest指向数组-1下标的位置。
刚开始dest先不动,因为cur是扫描数组的,所以先让cur先动。cur从前往后移动的过程中,会出现两种情况,遍历的元素是否为0:
当cur遇到0元素时,我们要保证 非0 0 未扫描 这三个区间的性质不变:
刚开始,cur指向0下标的数组元素,值为0 ;
在cur遇到0元素时,如cur当前遍历的0下标元素0。我们仅需要让cur向后移动一位即可。
cur向右移动一位,cur左边的区间[dest+1,cur-1]中,都是0元素。
所以cur在遍历到0元素,不做任何处理。
在cur遍历到非0元素时,我们让当前遍历的元素加入最左边的区间 [0 ,dest]
所以相当于 [0,dest] 这个区间中,多了一个元素,我们让dest往右移动一位:
移动好dest后,cur先不动,这时候交换cur和dest下标的元素:
所以cur当前遍历的元素,在交换后,相当于已经被处理过了,cur向后移动一位:
综上所述,如果cur遍历的元素是0元素,则不做任何处理;
如果cur遍历到非0元素时,[0,dest]区间就要多一个元素,所以让dest指针往后移动一位,cur先不动;
让dest指向的元素与cur指向的元素进行交换,cur++;以 cur<n 为循环终止条件。
3. 编写代码
对于这个题目,对cur指针的所有调整,都通过for循环完成,不用再在if语句中调整cur;
在cur遍历到0元素时,不做任何处理;所以,我们在for循环中,只要写cur遍历到非0元素的处理方法即可:
java
class Solution {
public void moveZeroes(int[] nums) {
int dest = -1;
for(int cur = 0;cur<nums.length;cur++){
if(nums[cur]!=0){
dest++;
int tmp=nums[cur];
nums[cur]=nums[dest];
nums[dest]=tmp;
}
}
}
}