LeetCode—283—Move Zeroes

题目

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.

Example:

Input: [0,1,0,3,12]
Output: [1,3,12,0,0]

翻译

给定一个数组nums,写一个函数,将数组中所有的0元素,挪到数组的末尾,而维持其他所有非0元素的相对位置。
举例: [0,1,0,3,12],函数运行后结果为[1,3,12,0,0]

如何解决

最直观的思路
首先我们扫描一遍数组,将所有非零元素全都拿出来。有几个这样的非零元素。就将这些元素填补到原先的数组中。之后将没填补的位置全都赋值为0;
1.png
取出所有非零元素
2.png
将非零元素填补到数组中
3.png

将没填补的位置全都赋值为0
代码如下:

import java.util.*; // 283. Move Zeroes // https://leetcode.com/problems/move-zeroes/description/ // 时间复杂度: O(n) // 空间复杂度: O(n) class Solution1 { public void moveZeroes(int[] nums) { ArrayList<Integer> nonZeroElements = new ArrayList<Integer>(); // 将vec中所有非0元素放入nonZeroElements中 for (int i = 0; i < nums.length; i++) if (nums[i] != 0) nonZeroElements.add(nums[i]); // 将nonZeroElements中的所有元素依次放入到nums开始的位置 for (int i = 0; i < nonZeroElements.size(); i++) nums[i] = nonZeroElements.get(i); // 将nums剩余的位置放置为0 for (int i = nonZeroElements.size(); i < nums.length; i++) nums[i] = 0; } } import java.util.*; // 283. Move Zeroes // https://leetcode.com/problems/move-zeroes/description/ // 时间复杂度: O(n) // 空间复杂度: O(n) class Solution1 { public void moveZeroes(int[] nums) { ArrayList<Integer> nonZeroElements = new ArrayList<Integer>(); // 将vec中所有非0元素放入nonZeroElements中 for (int i = 0; i < nums.length; i++) if (nums[i] != 0) nonZeroElements.add(nums[i]); // 将nonZeroElements中的所有元素依次放入到nums开始的位置 for (int i = 0; i < nonZeroElements.size(); i++) nums[i] = nonZeroElements.get(i); // 将nums剩余的位置放置为0 for (int i = nonZeroElements.size(); i < nums.length; i++) nums[i] = 0; } }

一级优化
上面这种解题方法虽然解决了问题。但是有没有更好的解决方法呢,我们分析上面的这种解题思路,在上一个方法中我们新开辟了一片空间,用于存储非零元素,所以他的空间复杂度为O(n),接下来这种解法,我们就用nums数组本身来实现。
先看下面这张图
5.png
在这里我们新定义一个索引 k,在[0…k)这个前闭后开的区间中存放所有遍历过的非0元素。我们开始遍历数组,当遍历的元素是非零元素时,就将元素放在k所在的位置,同时为了存放下一个非0元素。k要加一,继续遍历数组。
6.png

上图中遍历到第二个元素,为非0元素,所以要将1放到k所在的位置,此后k加一。
7.png
到我们遍历完数组时,此时区间[0…k)存放了所有非零元素,下来我们要做的就是从当前k开始,将剩余所有元素都赋值为0,从而完成整个操作,此时整个操作的空间复杂度为O(1)级别。
代码如下:

// 原地(in place)解决该问题 // 时间复杂度: O(n) // 空间复杂度: O(1) class Solution2 { public void moveZeroes(int[] nums) { int k = 0; // nums中, [0...k)的元素均为非0元素 // 遍历到第i个元素后,保证[0...i]中所有非0元素 // 都按照顺序排列在[0...k)中 for(int i = 0 ; i < nums.length ; i ++) if( nums[i] != 0 ) nums[k++] = nums[i]; // 将nums剩余的位置放置为0 for(int i = k ; i < nums.length ; i ++) nums[i] = 0; } } // 原地(in place)解决该问题 // 时间复杂度: O(n) // 空间复杂度: O(1) class Solution2 { public void moveZeroes(int[] nums) { int k = 0; // nums中, [0...k)的元素均为非0元素 // 遍历到第i个元素后,保证[0...i]中所有非0元素 // 都按照顺序排列在[0...k)中 for(int i = 0 ; i < nums.length ; i ++) if( nums[i] != 0 ) nums[k++] = nums[i]; // 将nums剩余的位置放置为0 for(int i = k ; i < nums.length ; i ++) nums[i] = 0; } }

二级优化
通过前面的优化虽然我们在原地完成了move zeros的操作。但是当我们将所有的非0元素都挪到数组的前面时。我们还要花费一些操作,将剩余元素全都赋值为0。那么显而易见的想法是将所有非零元素放好位置时,相应的0元素已经放在了后面。其实我们可以通过将非0元素和0元素交换位置,从而达到这个目的。
8.png
如上图,此时遍历到第四个元素,此时为非0元素。所以将第四个元素和k所在的元素交换位置,变成下面这幅图,最终完成整个操作。
9.png
代码如下:

// 原地(in place)解决该问题 // 时间复杂度: O(n) // 空间复杂度: O(1) class Solution3 { public void moveZeroes(int[] nums) { int k = 0; // nums中, [0...k)的元素均为非0元素 // 遍历到第i个元素后,保证[0...i]中所有非0元素 // 都按照顺序排列在[0...k)中 // 同时, [k...i] 为 0 for(int i = 0 ; i < nums.length ; i ++) if(nums[i] != 0) swap(nums, k++, i); } private void swap(int[] nums, int i, int j){ int t = nums[i]; nums[i] = nums[j]; nums[j] = t; } } // 原地(in place)解决该问题 // 时间复杂度: O(n) // 空间复杂度: O(1) class Solution3 { public void moveZeroes(int[] nums) { int k = 0; // nums中, [0...k)的元素均为非0元素 // 遍历到第i个元素后,保证[0...i]中所有非0元素 // 都按照顺序排列在[0...k)中 // 同时, [k...i] 为 0 for(int i = 0 ; i < nums.length ; i ++) if(nums[i] != 0) swap(nums, k++, i); } private void swap(int[] nums, int i, int j){ int t = nums[i]; nums[i] = nums[j]; nums[j] = t; } }

最终优化
算法的世界里没有最好,只有更好。在上面这个问题中,那么有没有一种可能就是nums数组全是非零元素。那如果这样的话。我们遍历一遍,就相当于每个元素自己和自己交换了一遍位置。那么这是我们不希望看到的。那么在这里我们还需要加入一个特殊的判断。优化后的代码如下:

// 原地(in place)解决该问题 // 时间复杂度: O(n) // 空间复杂度: O(1) class Solution4 { public void moveZeroes(int[] nums) { int k = 0; // nums中, [0...k)的元素均为非0元素 // 遍历到第i个元素后,保证[0...i]中所有非0元素 // 都按照顺序排列在[0...k)中 // 同时, [k...i] 为 0 for(int i = 0 ; i < nums.length ; i ++) if(nums[i] != 0) if(k != i) swap(nums, k++, i); else k ++; } private void swap(int[] nums, int i, int j){ int t = nums[i]; nums[i] = nums[j]; nums[j] = t; } } // 原地(in place)解决该问题 // 时间复杂度: O(n) // 空间复杂度: O(1) class Solution4 { public void moveZeroes(int[] nums) { int k = 0; // nums中, [0...k)的元素均为非0元素 // 遍历到第i个元素后,保证[0...i]中所有非0元素 // 都按照顺序排列在[0...k)中 // 同时, [k...i] 为 0 for(int i = 0 ; i < nums.length ; i ++) if(nums[i] != 0) if(k != i) swap(nums, k++, i); else k ++; } private void swap(int[] nums, int i, int j){ int t = nums[i]; nums[i] = nums[j]; nums[j] = t; } }

更多内容欢迎大家关注

yuandatou