【算法】移动零

103 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第30天,点击查看活动详情

题目

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

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

解题思路

关键词:0移动到数组末尾;保持相对顺序;

关键信息:需要保持相对顺序;

低效双指针解法

数组排序位移最先想到的还是双指针解法。利用首尾指针遍历数组将首指针的0后移到尾部。

  1. 创建双指针初始化分别指向0,nums.length - 1。
  2. 遍历右指针直到不为零为止。
  3. 遍历左指针直到为零为止。
  4. 两个条件都满足就交换两个值。
  5. 遍历完成后再判断是否还有剩余数值填充到数组最后。
public void moveZeroes(int[] nums) {
            int i = 0,j = nums.length - 1; // 左右指针分别先指向数组首和尾部
            while(i < j){
                while(nums[j] == 0) j--;
                while(nums[i] != 0) i++;
                if(nums[i] == 0){
                    nums[i++] = nums[j];
                    nums[j--] = 0;
                }
            }
        	// 剩余值遍历
             int i = 0,j = 1,length = nums.length; 
            while(i < j && j < length){
                while(nums[j] != 0) j++;
                while(nums[i] != 0) i++;
            }
    }

该解法可以实现将0移动到数组后方,但根据题意“保持非零元素的相对顺序”没有实现。若要保证相对顺序使用双指针首尾判断就不合适了。

保持相对顺序解法双指针方式就是指针依靠。i和j相邻,i做步进操作,j在i之后做遍历直到i遍历结束。

  1. i步进到0数值,j=i+1接着遍历j直到非0为止。
  2. 交换i和j数值,i++继续步进到非0。
  3. 重复上述步骤直到遍历结束。
 public void moveZeroes(int[] nums) {
        int i = 0,j = i + 1;
        while(i < nums.length){
           while(i < nums.length && nums[i] != 0) i++; // 为0为止
           j = i + 1;
           while(j < nums.length && nums[j] == 0) j++; // 非0为止
           if(j < nums.length && i < nums.length){
              nums[i] = nums[j];
              nums[j] = 0;
           }
           i++;
        }
    }

高效解法

高效解法思路正好和上述解法相反,通过移动right指针找到非0数值将其和left指针值做交换。因为left和right都是以0下标初始化,因此right移动肯定不会忽略过任何为0数值。left指向值肯定都是0的数值。

  1. left和right初始化指针指向0下标
  2. 循环过程当right小于数组长度
  3. right指向不为0的情况时对left和right做交换,同时两个指针都前移
 public void moveZeroes(int[] nums) {
		int n = nums.length, left = 0, right = 0;
        while (right < n) { // 小于数值长度
            if (nums[right] != 0) { // 右边不为0时 左右替换
                int temp = nums[left]; 
                nums[left] = nums[right];
                nums[right] = temp;
                left++; // 左边指针移动
            }
            right++; //右边指针移动
        }
 }

参考