move zeroes --java 解法 同leetcode 283

题目是我做一个公司的笔试题目出现的,后来发现leetcode 上也有。
参考:leetcode 283

Given an array nums, write a function to move all 0’s to the end of it while maintaining the relative order of the non-zero elements.

For example, given nums = [0, 1, 0, 3, 12], after calling your function, nums should be [1, 3, 12, 0, 0].

Note:
You must do this in-place without making a copy of the array.
Minimize the total number of operations.

题目描述

给定一个数组,将数组中的0元素全部移动到数组的尾部,并保持原数组的非0元素的在数组中出现的顺序。
例如 输入nums = [0, 1, 0, 3, 12], 输出[1, 3, 12, 0, 0]。

要求

1.只能对数组进行原地操作,不能额外借用数组或者复制数组;
2.时间复杂度越小越好。

思路

在不允许使用辅助数组的情况下,只能对数组本身进行操作。根据题意,由于我们要找出数组中的0元素和非0元素,显然,我们需要两个指针来分别标记他们,然后交换两指针上的值来达到目的。
接下来的三种方法,无论哪一种思路,实际上都是用到了两种指针来标记0元素和非0元素的目的。

方法一

顺序扫描数组,用一个指针iPointer从0开始,可以理解为标记数组中非0元素的个数,遇到非0元素时,顺序的直接将其移动到数组的前面即iPointer所指位置,数组扫描完毕时,iPointer指向数组中的最后一个非0元素。然后将iPointer后面的部分的数组元素全部补0即可。这种思路的代码也最容易理解。

class Solution {
    public void moveZeroes(int[] nums) {
        //方案一
        int iPointer = 0;
        int len = nums.length;
        for(int i=0; i<len;++i)
        {
            if(nums[i] != 0)
                nums[iPointer++] = nums[i];//直接将遇到的非0元素
        }
        if(iPointer != 0)
        {
            for(; iPointer<len; iPointer++)
                nums[iPointer] = 0;
        } 
   }
}
方法二

实际上算是方案一的改进版,既然每次找到了非0元素,干嘛不多用一个指针,标记一下0元素,让其两者直接交换,而无须在数组扫描完毕后再对数组剩余部分置0了。
流程:iPointer=0用来从头扫描数组,jPointer=0用来标记0元素。两者都从头开始移动指针。IPointer碰到非0元素时停下,将其跟jPointer指向的0元素交换,交换完,j向前移动一个位置,由于iPointer扫描数组时是遇到非0元素才停下,交换完后,jPointer移动一个指针指向的肯定是0元素,实际上在交换元素的过程中,i,j指针之间的元素一定都是0

class Solution {
    public void moveZeroes(int[] nums) {

        if(nums == null || nums.length == 0)
        {
            return;
        }

        int len = nums.length;
        for(int i=0, j=0; i < len; ++i)
        {
            if(nums[i] != 0 )//i指针实际上依次指向非0元素时,才有交换
            {
                /*nums[j] = nums[i];//交换完,将j指针指向0元素 nums[i] = 0; ++j;*///这样做的问题就在于当数组第一个元素或者都是非0元素时就会出现问题

                /*nums[i] = nums[i] + nums[j];//或者用异或a=a^b;b=a^b;a=a^b; nums[j] = nums[i] - nums[j]; nums[i] = nums[i] - nums[j];*///这个只适合交换数组中不同位置上的数字,如果i=j时就会改变nums[i]的值

                int temp = nums[i];
                nums[i] = nums[j];
                nums[j] = temp;
                j++;//i,j指针交换完,将j指针指向0元素
            }
        }       
    }
}
方法三

实际上思路跟方法二完全一样。

class Solution {
    public void moveZeroes(int[] nums) {

        /** *思路:维持两个指针:一个指向非0元素(iPointer),一个指向0元素(jPointer) * 一开始i,j指针同时都从0开始往后移动; * 先移动i指针遇到非0元素停下,j指针遇到0元素停下; * 交换i,j指针上的值,继续移动; * 我们会发现:i,j指针之间都是0元素 */
        int len = nums.length;
        int iPointer = 0;
        int jPointer = 0;
       while (iPointer < len)
        {
            if(nums[iPointer] != 0)//在遇到0元素时,只有i指针移动
            {
                if (iPointer != jPointer) {
                    nums[jPointer] = nums[iPointer];
                    nums[iPointer] = 0;
                    jPointer++;
                } else {
                    jPointer++;//在遇到非0元素时,i,j指针同时移动
                }

            }
            iPointer++;//在遇到0元素时,只有i指针移动////在遇到非0元素时,i,j指针同时移动
        }        
    }
}

通过比较三种方法,实质上都是通过双指针来完成原地操作的,之所以写第三种方法,是我们能更直观的看到双指针的操作。以上三种方法都在leetcode上Accepted的。双指针的思路解决办法感觉还是很重要的。

点赞