leetcode--java--移动零

题目描述

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

示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]

说明:
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。

我的解法

class Solution {
    public void moveZeroes(int[] nums) {
        int temp;
        //冒泡排序不改变数组元素相对顺序
        for(int i=nums.length-1;i>=0;i--){
            if(nums[i]==0){
                //遇到0,直接让其沉底
                for(int j=i;j<nums.length-1;j++){
                        temp = nums[j];
                        nums[j] = nums[j+1];
                        nums[j+1] = temp;
                }
            }
        }
    }
}

在这里插入图片描述
我的思路:遇到0则让0沉底。时间复杂度太高了。
这还有个小心机:如果外循环从小到大的话,那不行,都不知道0后面的数是啥,可能还是0与0交换。
这里外循环是从大到小,就保证了每次循环都是正确的。

官方解法

方法一

使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部

右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。

注意到以下性质:

左指针左边均为非零数;

右指针左边直到左指针处均为零。

因此每次交换,都是将左指针的零与右指针的非零数交换,且非零数的相对顺序并未改变。

遇到0则右指针右移,左指针不动,
遇到非0的数,交换两指针的数,然后两个指针都右移,
如果一直没有0,那么左右指针一直指向同一个数,自己和自己交换

class Solution {
    public void moveZeroes(int[] nums) {
        int n = nums.length, left = 0, right = 0;
        while (right < n) {
            if (nums[right] != 0) {
                swap(nums, left, right);
                left++;
            }
            right++;
        }
    }

    public void swap(int[] nums, int left, int right) {
        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
    }
}

在这里插入图片描述
方法二:
这里参考了快速排序的思想,快速排序首先要确定一个待分割的元素做中间点x,
然后把所有小于等于x的元素放到x的左边,大于x的元素放到其右边。
这里我们可以用0当做这个中间点,把不等于0(注意题目没说不能有负数)的放到中间点的左边,等于0的放到其右边。
这的中间点就是0本身,所以实现起来比快速排序简单很多,我们使用两个指针i和j,只要nums[i]!=0,我们就交换nums[i]和nums[j]

class Solution {
	public void moveZeroes(int[] nums) {
		if(nums==null) {
			return;
		}//考虑空指针
		//两个指针i和j
		int j = 0;
		for(int i=0;i<nums.length;i++) {
			//当前元素!=0,就把其交换到左边,等于0的交换到右边
			if(nums[i]!=0) {
				int tmp = nums[i];
				nums[i] = nums[j];
				nums[j++] = tmp;
			}
		}
	}
}	

在这里插入图片描述

在这里插入图片描述
细节处理

/*已知是0,不交换,直接赋值*/
    public void moveZeroes(int[] nums) {
        int indexNow = 0;        //已处理好的序列的尾部
        int indexNum = 0;       //待处理序列的头部
        int m = nums.length;
	    //非0则两个指针都++,0则移动待处理指针即可
        while(indexNum<m){
            if(nums[indexNum] != 0) {
                nums[indexNow++] = nums[indexNum];
            }
            ++indexNum;
        }
        for(int i = indexNow; i < m; i++){
            nums[i] = 0;
        }
    }

/*将右指针变成for循环*/
public void moveZeroes(int[] nums) {
		if(nums==null) {
			return;
		}
		//两个指针i和j
		int j = 0;
		for(int i=0;i<nums.length;i++) {
			//当前元素!=0,就把其交换到左边,等于0的交换到右边
			if(nums[i]!=0) {
				int tmp = nums[i];
				nums[i] = nums[j];
				nums[j++] = tmp;
			}
		}
}

剖析

这两种方法都算是双指针吧,没什么本质差别。
一个指针记录排好序的,一个记录未排好序的,数据从一端到另一端。
这种解决问题的思路值得借鉴。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值