今天是小呆刷题的第4天,今天的题目是:力扣(LeetCode)的第283题,移动零

题目要求

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

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

示例:

1
2
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]

提示:

  • 1 <= nums.length <= 10^4
  • -2^31 <= nums[i] <= 2^31 - 1

解题思路

刷题的第4天,遇到这种原地操作数组的题,看起来要求跟第27题移除元素很相似,唯一的区别是把数组里所有的0移到末尾。小呆首先的思路是考虑用双指针算法,所以第一步是先将非0的元素移到前面。以示例代码为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* @param {number[0, 1, 0, 3, 12]} nums
* @return {void} Do not return anything, modify nums in-place instead.
*/
var moveZeroes = function(nums) {
if(nums.length === 0) return // 老规矩先判断边界值
let fast = 0, slow = 0
while(fast < nums.length) {
if(nums[fast] !== 0) {
nums[slow] = nums[fast]
slow++
}
fast++
}
return nums
}
console.log(moveZeroes[0, 1, 0, 3, 12]) // [1, 3, 12, 3, 12] 运行到这里,fast:5 slow:3

从打印结果可以看出,非0的代码已经都移到前面了,只需要从slow开始,到数组末尾的数字替换为0即可,于是有了下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @param {number[0, 1, 0, 3, 12]} nums
* @return {void} Do not return anything, modify nums in-place instead.
*/
var moveZeroes = function(nums) {
if(nums.length === 0) return // 老规矩先判断边界值
let fast = 0, slow = 0
while(fast < nums.length) { // 第一轮循环用于把非0的元素移到前面
if(nums[fast] !== 0) {
nums[slow] = nums[fast]
slow++
}
fast++
}
while(slow < nums.length) { // 第二轮循环用于把剩余的元素替换为0
nums[slow] = 0
slow++
}
return nums
}
console.log(moveZeroes[0, 1, 0, 3, 12]) // [1, 3, 12, 0, 0]

老规矩,上动图帮助理解,加深记忆:

移动零

上面的代码写了2个while循环,那能不能只写一种循环呢?我们来改动代码试试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @param {number[0, 1, 0, 3, 12]} nums
* @return {void} Do not return anything, modify nums in-place instead.
*/
var moveZeroes = function(nums) {
if(nums.length === 0) return // 老规矩先判断边界值
let fast = 0, slow = 0
while(slow < nums.length) {
if(fast >= nums.length) {
nums[slow] = 0
slow++
} else {
if(nums[fast] !== 0) {
nums[slow] = nums[fast]
slow++
}
fast++
}
}
return nums
}
console.log(moveZeroes[0, 1, 0, 3, 12]) // [1, 3, 12, 0, 0]

while循环的条件从fast变为了slow,当fast的位置超出数组最后一位时,说明所有非0的元素已经都移位好了,此时只需要将剩余的元素设为0即可。下面是两种方式LeetCode提交代码的结果,仅供参考。

执行用时 内存消耗
两个循环 104ms 45.1MB
一个循环 84ms 45.8MB

小结

到目前为止已经刷题4天了,每天一道题,数量虽然不多,但是最令小呆享受的是制作动图的过程,虽然目的是为了方便看这篇文章的同学便于理解,但无形之中也是对于小呆自身的一个理解强化。如果看这篇文章的你也跟我一样算法是短板,那就跟我一起坚持下去吧,期待花开结果的那天~

引用

力扣LeetCode的第283题-移动零

双指针技巧秒杀七道数组题目——作者:labuladong