问题描述
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
输入输出
示例 1:
输入: nums = [0,1,0,3,12] 输出: [1,3,12,0,0]
示例 2:
输入: nums = [0] 输出: [0]
提示:
1 <= nums.length <= 104
-231 <= nums[i] <= 231 - 1
进阶:你能尽量减少完成的操作次数吗?
解题思路
方法一:暴力法。直接从左往右遍历数组,每找到一个0,就把它右边的数全部逐一移到左边。虽然很直接,但是时间复杂度。既然都做算法题了,肯定不考虑。
方法二:计步数组。即用一个新数组表示原数组的值需要向左移多少步,可以通过遍历原数组,每找到一个0,下标为i的话,计步数组下标比i大的就全部+1。还需要记录0的个数,最后补在原数组后面。虽然听着很奇特,但是时间复杂度还是,而且有点钻题目漏洞的感觉,题目不给复制原数组的意思就是不给用新数据结构,需要对原数组进行操作,毕竟用新数组的话直接将非零值复制过去更快。
方法三:双指针法。本质是利用两个指针标识0序列的头和尾+1,通过交换头和尾+1,实现0序列这个子序列在原数组中的移动,如下图。时间复杂度为,因为只遍历了一遍数组。
方法三演示:结合上面的说明、下面的代码示范一下对数组 [2,3,5,0,1,0,3,12] 进行操作的流程。
AC代码
/* public class Main {
public static void main(String[] args) {
Solution solution = new Solution();
solution.moveZeroes(new int[]{2, 3, 5, 0, 1, 0, 3, 12});
}
} */
class Solution {
public void moveZeroes(int[] nums) {
int n = nums.length, left = 0, right = 0; // 左、右指针
while (right < n) {
if (nums[right] != 0) {
// 交换
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
left++;
}
right++;
/*
System.out.println("left: " + left + " right: " + right + " ");
for (int num : nums) {
System.out.print(num + " ");
}
System.out.println();
System.out.println(); */
}
}
}
优化点
1.不要单独写swap方法来交换值,如果是大项目的话为了解耦可以理解,但是这种小程序单独提取一个方法反而增加耗时,如下对比所示,写swap方法2ms,不写为1ms。(均多次提交验证)
2.交换前判断left和right是否相等,若相等则指向的是同一个值,说明还没找到第一个0,此时没必要自己和自己交换值。
但是问题又来了,这个判断在找到第一个0后就没有意义了,相当于在做无用功,因为找到第一个0后两个指针的值一定是不同的。
对此可以添加一个信号值flag初始为0,找到第一个0后设置为1,再根据信号值决定是否判断left和right是否相等(不过好像判断信号值和判断这二者是否相等差别不大,只不过读一个变量和两个变量的区别,差别其实微乎其微了)。
这个优化针对极端数据(如第一个0前有很多数)或者大量数据时效果才会比较明显,小数据的话区别不大。
/* public class Main {
public static void main(String[] args) {
Solution solution = new Solution();
solution.moveZeroes(new int[]{2, 3, 5, 0, 1, 0, 3, 12});
}
} */
class Solution {
public void moveZeroes(int[] nums) {
int n = nums.length, left = 0, right = 0; // 左、右指针
while (right < n) {
if (nums[right] != 0) {
if (left != right) {
// 交换
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
left++;
}
right++;
/*
System.out.println("left: " + left + " right: " + right + " ");
for (int num : nums) {
System.out.print(num + " ");
}
System.out.println();
System.out.println(); */
}
}
}
(by 归忆)