题目描述
题目链接:283.移动零
给定一个数组 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
上面,我们可以想怎么排非零数
解法一:暴力冒泡
顾名思义,碰到0
就把它冒泡到最后,记录一下冒泡的次数控制循环即可
public void moveZeroes(int[] nums) {
int num = 0;//记录要移动的0的数量
for (int i = 0; i < nums.length - 1; i++) {
if (i + 1 == nums.length - num) {
break;//当前索引位置等于总长度减去移动的0的数量即可停止循环
}
if (nums[i] == 0) {
num++;
for (int j = i; j < nums.length - 1; j++) {//将当前元素冒泡到最后一个位置
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
i--;//因为后一位元素前移了,所以当前位置要再次判断
}
}
}
解法二:数组拷贝
虽然本题说了不可以使用额外空间,让我们在数组原地操作,但是我们人是活的,这样也是一种方法,我们可以从这个方法去衍生出原地操作的方法
public void moveZeroes(int[] nums) {
int index = 0;
int[] arr = new int[nums.length];
for (int i : nums) {
if (i != 0) {
arr[index++] = i;
}
}
System.arraycopy(arr, 0, nums, 0, nums.length);
}
解法三:双指针
双指针索引非零数
然后覆盖即可,因为0
需要被放置在末尾,所以我们覆盖的位置一定是已经判断过的(当前遍历的元素个数 - 0的个数) - 1 = 结果非零数的索引)
,不用担心覆盖掉其他非零数,判断完成后将剩余的位置补0即可
public void moveZeroes(int[] nums) {
int pIndex = 0;//指向索引
int pNum = 0;//指向结果
while (pIndex != nums.length) {
if (nums[pIndex] != 0) {
nums[pNum++] = nums[pIndex];
}
pIndex++;
}
for (int i = pNum; i < nums.length; i++) {
nums[i] = 0;
}
两种写法本质都是一样的,只是下面的内存空间会小一些,区别不大
public void moveZeroes(int[] nums) {
int pNum = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] != 0) {
nums[pNum++] = nums[i];
}
}
for (int i = pNum; i < nums.length; i++) {
nums[i] = 0;
}
}
解法四:控制索引
(当前遍历的元素个数 - 0的个数) - 1 = 结果非零数的索引
这个关系非常重要,我们可以根据这个关系来一次遍历,遇到非零数
我们就交换它和0
的位置
public void moveZeroes5(int[] nums) {
int k = 0;//记录0的个数
for (int i = 0; i < nums.length; i++){
if (nums[i] == 0){
k++;
}else if(k > 0){
//[(i+1)-k]-1:(当前遍历的元素个数 - 0的个数) - 1 = 结果非零数的索引
nums[i-k] = nums[i];
nums[i] = 0;
}
}
}