一、题目
Given an array nums, write a function to move all 0’s to the end of it while maintaining the relative order of the non-zero elements.
For example, given nums = [0, 1, 0, 3, 12], after calling your function, nums should be [1, 3, 12, 0, 0].
Note:
You must do this in-place without making a copy of the array.
Minimize the total number of operations.
二、分析和解答
题意要求:把0值放在数组的后面,保持非0值的顺序保持不变。但要in-place进行排序,不能进行复制。
1、我的想法:
(1)找到非零值 的个数
(2)把非零值提前
(3)后面补零
代码如下:
public void moveZeroes(int[] nums) {
int count = 0;
for(int i=0;i<nums.length;i++){
if(nums[i] != 0)
count++;
}
int t = 0;
while(t < count){
for(int j=0;j<nums.length;j++){
if(nums[j] != 0){
nums[t] = nums[j];
t++;
}
}
}
for(int j=count;j<nums.length;j++){
nums[j] = 0;
}
}
AC
2、我回想的一个算法,如何不消耗空间来交换两个数的值?使用异或操作。即针对同一变量异或两次,值不变。
3、别人代码:对第一二步进行优化。压缩非0值,也是把它迁移,但是这个for循环很巧妙。
public void moveZeroes(int[] nums) {
int count = 0;
for(int i=0;i<nums.length;i++){
if(nums[i] != 0){
nums[count] = nums[i];
count++;
}
}
for(int j=count;j<nums.length;j++){
nums[j] = 0;
}
}
补充:这个也是快慢指针的意思。一个指针不停地向后扫描,找到非零位置,然后和前面的那个指针count交换位置即可,我这种也对。
4/别人代码:保持两个指针,一个指向0值,一个指向非零值,当正好一对的时候(0,非0),进行交换。我未成功,下一遍再补充吧
public void moveZeroes(int[] nums) {
int zero = 0;
int non = 0;
while(zero<nums.length && non<nums.length){
while(zero<nums.length && nums[zero] != 0)
zero++;
while(non<nums.length && (nums[non] == 0 || non < zero))
non++;
if(zero<nums.length && non<nums.length){
nums[zero] = nums[zero]^nums[non];
nums[non] = nums[zero]^nums[non];
nums[zero] = nums[zero]^nums[non];
}
}
}
(1)while(zero<nums.length && nums[zero] != 0)
前后两个条件不能交换,必须是这个顺序,因为后面后zero++,当zero达到nums.length的时候,在数组里面肯定就越界了,这样可以防止越界!快排里面也是这个道理。
(2)while(non<nums.length && (nums[non] == 0 || non < zero))
这两个也得保证顺序。但是一般人肯定都会忽略non < zero这个条件,所以案例中会出现[1,0]。zero开始会指向0值,non会指向1值,然后交换输出成了[0,1],显然这是错误的,因此在这种情况下,让non继续向前走就是了。这是什么情况呢?其实就是本身就是正确顺序的情况,non的指针会在zero的前面,1在0前面本身就正确。
(3)两个数的交换要使用异或的方法在原地进行交换,不会使用额外的空间,前面的那种交换使用了额外的空间,是错误的。
(4)其实追根到底,这题和快排都属于那种使用两个快慢指针来进行操作的情况,这是一种类型的题,以后要注意了。
补充:注意了,这里while的条件和快排里面的可是不一样的,这里是小于长度,快排是low < high。这里是因为这两个指针都是从前向后遍历,因此比较的是数组最后的那一位;而快排是两个指针从两边向中间移动,最后会到达中间的位置,因此比较的是low和high。