http://www.lintcode.com/zh-cn/problem/move-zeroes/
1.问题描述
给一个数组 nums写一个函数将0移动到数组的最后面,非零元素保持原数组的顺序
注意事项
1.必须在原数组上操作
2.最小化操作数
2.样例
给出 nums = [0, 1, 0, 3, 12], 调用函数之后, nums = [1, 3, 12, 0, 0].
3.问题解析
思路一:从头开始遍历数组,遇到数字0,将区间i + 1 ~ nums.length - count - 2的元素左移一位,统计数字0的个数:count。
public static void moveZeroes(int[] nums) {
if(nums == null || nums.length <= 1) return;
int count = 0;// 0的个数
for(int i = 0;i < nums.length - count - 1;i++){
if(nums[i] == 0){
for(int j = i;j < nums.length - count - 1;j++){
nums[j] = nums[j + 1];// 左移一位
}
nums[nums.length - count - 1] = 0;
count++;
i--;
}
}
}
这种解决方案,仅耗费一个整数的内存空间,测试在10万级别数据量以上,涉及到大量的数组元素左移,非常耗时间,测试10万的数据 : 900ms。
思路二:循环数组nums,用一个新的数组arr存储数字不为0的整数,最后用原生的数组拷贝将arr内存数据拷贝到nums。
public static void moveZeroes(int[] nums){
if(nums == null || nums.length <= 1) return;
int index = 0;
int[] arr = new int[nums.length];
for(int num : nums){
if(num != 0) arr[index++] = num;
}
System.arraycopy(arr, 0, nums, 0, nums.length);
}
这种解决方案,需要耗费和数组nums同大小的内存空间:数组arr,仅一次遍历,时间复杂度O(n),测试10万的数据:2ms,1000万的数据 : 40ms。
思路三:循环数组nums,用一个新的数组zeroIndexs存储数字为0的整数的索引,数字0后的元素整体均只移动一次。
public static void moveZeroes(int[] nums){
if(nums == null || nums.length <= 1) return;
int count = 0;
int[] zeroIndexs = new int[nums.length];
for(int i = 0;i < nums.length;i++){
if(nums[i] == 0){
zeroIndexs[count++] = i;
}
}
if(count == 0 || zeroIndexs[0] == nums.length - count) return;
int step = 1;//元素左移的位数
for(int i = 0;i < count - 1;i++){
for(int j = zeroIndexs[i] + 1;j < zeroIndexs[i + 1];j++){
nums[j - step] = nums[j];// 左移
}
step++;
}
// 最有一个区间,如果是右开放的区间,移动元素
if(zeroIndexs[count - 1] != nums.length - 1){
for(int i = zeroIndexs[count - 1] + 1;i < nums.length;i++){
nums[i - step] = nums[i];
}
}
// 元素移动完成,需要将右侧的元素设为0
for(int i = nums.length - count;i < nums.length;i++){
nums[i] = 0;
}
}
这种解决方案,需要耗费和数组nums同大小的内存空间:数组arr。时间复杂度O(n),测试10万的数据:4ms,1000万的数据 : 80ms。
4.测试数据
int[] nums = new int[10000000];
for(int i = 1;i <= 10000000;i++){
nums[i - 1] = i % 2 == 0 ? 0 : i;
}
long start = System.currentTimeMillis();
moveZeroes(nums);
System.out.println(System.currentTimeMillis() - start);