【双指针】【简单】移动零

题目

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:

  • 输入: nums = 0,1,0,3,12

  • 输出: 1,3,12,0,0
    示例 2:

  • 输入: nums = 0

  • 输出: 0
    提示:

  • 1 <= nums.length <= 104

  • -231 <= numsi <= 231 - 1

进阶:你能尽量减少完成的操作次数吗?

解法1:直接复制

  • 直接找一个新的数组,按顺序复制非零数字即可,空出来的位置为0
java 复制代码
public class Move_Zero {
    public static void moveZeroes(int[] nums) {
        int[] news = new int[nums.length];
        int j = 0;
        for (int i: nums){
            if (i != 0){
                news[j] = i;
                j++;
            }
        }
        System.out.println(Arrays.toString(news));
    }
}

解法2:双指针交换

  • 寻找到第一个为0的位置
  • 在该位置之后寻找第一个不为0的数据
  • 二者进行交换
bash 复制代码
{0,1,0,0,12};arr[0]为0,下一个不为0的是arr[1],交换
{1,0,0,0,12};arr[1]为0,下一个不为0的是arr[4],交换
{1,12,0,0,0};arr[2]为0,下一个不为0的是不存在,遍历到末尾,直接break


{0,3,4};arr[0]为0,下一个不为0的是arr[1],交换
{3,0,4};arr[1]为0,下一个不为0的是arr[2],交换
{3,4,0};break
java 复制代码
public class Move_Zero {
    public static void moveZeroes(int[] nums){
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == 0) {//i的位置是第一个为0的位置
                int first_not_zero = i;
                //向后依次遍历首个不为0的元素
                for (first_not_zero = i; first_not_zero < nums.length; first_not_zero++) {
                    if (nums[first_not_zero] != 0) {
                    //寻找到后进行交换
                        int temp = nums[first_not_zero];
                        nums[first_not_zero] = 0;
                        nums[i] = temp;
                        break;
                    }
                }
                if (first_not_zero == nums.length) break;//之后判断都是0,直接break
            }
        }
        System.out.println(Arrays.toString(nums));
    }
}

如果是这样的一个数组0,0,0,0,1,2,3,4,这样交换的方法,每次遇到0都需要向后遍历,时间复杂度为O(n^2)

解法3:双指针交换

  • 针对上面的解法,可以做一个改动,即第二个寻找非零位置的指针,不进行从头遍历
  • 首先找到第一个零元素位置和其后面的第一个非零元素位置
  • 交换后,非零元素寻找下一个非零元素,直至数组末尾

更为简洁的代码

  • 初始位置全部设为0
  • 当前位置 i 只要遍历到不是 0 的,就和 j 进行交换
bash 复制代码
java 复制代码
public void moveZeroes(int[] nums) {
    int j = 0; // 指向第一个0(或待交换位置)
    for (int i = 0; i < nums.length; i++) {
        if (nums[i] != 0) {
            if (i != j) {
                int temp = nums[i];
                nums[i] = nums[j];
                nums[j] = temp;
            }
            j++;
        }
    }
}

解法4:双指针覆盖补零

  • 上面的解法好像每次遍历交换有些复杂,可以直接进行数字的覆盖,这样只需要遍历一遍数组
  • 首部指针用来指向所有非零的元素,全遍历指针用来寻找非零的数字作为要覆盖的数据
  • 全遍历指针遍历完成后,首部指针后面的元素全为0
java 复制代码
public class Move_Zero {
    public static void moveZeroes(int[] nums){
        int first = 0;
        for(int i=0;i<nums.length;i++){
            if(nums[i]!=0){
                nums[first++]=nums[i];
            }
        }
        for(int i=first;i<nums.length;i++){
            nums[i]=0;
        }
        System.out.println(Arrays.toString(nums));
    }
}