LeetCode刷题笔记-283.移动零(move-zeroes)
问题描述
给定一个数组nums,编写一个函数将所有0移动到数组的末尾,同时保持非零元素的相对顺序。
说明:
- 必须在原数组上操作,不能拷贝额外的数组。
- 尽量减少操作次数。
示例 1:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
示例 2:
输入: [0,1]
输出: [1,0]
题解
1.双指针解法
算法解析
该算法使用一前一后pre和post两个指针,其中pre指针指向已经处理好的序列(全是非零数字)的尾部,即该指针指向的数组位置的元素一定为0;而post指针指向待处理的序列(包括零和非零数字)的头部,即此时post指针指向的元素可能为0和非0.而在这两个指针之间的元素一定为0.
因此,post指针的作用就是向后移动寻找到非0元素,然后与pre指针指向的0相交换.当post指向数组尾部(不包括最后一个元素,即越界)时,由于pre和post指针之间的元素一直保持为0,而pre之前的元素经过交换后全为非0,所以就完成题解.并且,由于每次交换,都是将pre指针的0与post指针的非0数交换,则非0数的相对顺序也不会改变。
参考文章及资料
LeetCode官方题解
LeetCode精选题解 一次遍历部分,快排的思想角度很独特.
复杂度分析
- 时间复杂度: \(O(N)\), 双指针总计最多遍历整个数组一次,即\(2N\)次
- 空间复杂度: \(O(1)\), 只需要固定的额外空间
代码实现
- Java版
class Solution {
public void moveZeroes(int[] nums) {
int n = nums.length;
if ((n == 0) || (n == 1)) return;
int pre = 0, post = 0;
int temp;
while (post < n) {
// 当post指针指向元素不为0时
// 就交换pre指向的0和post指向的非0
if (nums[post] != 0) {
temp = nums[post];
nums[post] = nums[pre];
nums[pre] = temp;
pre++; // 向后指向下一个0
}
post++; // 向后寻找非0元素
}
}
}
- Python版本
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n = len(nums)
if n == 0 or n == 1:
return
pre = post = 0
while post < n:
if nums[post]:
nums[pre], nums[post] = nums[post], nums[pre]
pre += 1 # 自增1 找到第一个为0的下标
post += 1 # 自增1 找到非0数下标
2.移动非0元素解法
算法解析
该方法与双指针解法类似,该方法使用insertPos指针,该指针功能类似于双指针解法的pre指针,但是其指向元素不一定为0,而另一个指针i功能类似于双指针解法的post指针,向后寻找非0数,但是,在找到非0数后并不是将两个指针指向的元素交换,而是将insertPos指针指向的数组位置赋值为i指针指向的非0数,这样就实现了非0数想起移动.因此,insertPos指针和i指针之间的元素就不再和双指针解法里一样全为0了,0和非0都存在.
当i指针指向的数组尾部(不包括最后一个元素,即越界)时,数组中全部元素都为非0,而insertPos指针指向的元素及其之后的元素在逻辑上就都应该是之前数组中的0,因此只需将它们置0即可.
参考文章及资料
LeetCode精选题解 两次遍历部分
LeetCode精选题解 把非0往前挪部分
LeetCode-Discuss Simple O(N) Java Solution Using Insert Index
复杂度分析
- 时间复杂度: \(O(N)\), 同双指针解法
- 空间复杂度: \(O(1)\), 同双指针解法
代码实现
- Java版(国际站讨论代码)
// Shift non-zero values as far forward as possible
// Fill remaining space with zeros
public void moveZeroes(int[] nums) {
if (nums == null || nums.length == 0) return;
int insertPos = 0;
for (int num: nums) {
if (num != 0) nums[insertPos++] = num;
}
while (insertPos < nums.length) {
nums[insertPos++] = 0;
}
}
- C版本
void moveZeroes(int* nums, int numsSize)
{
int i = 0, insertPos = 0;
for (; i < numsSize; ++i)
{
if (nums[i]) nums[insertPos++] = nums[i];
}
while (insertPos < numsSize) nums[insertPos++] = 0;
}
解题误区及心得总结
错误代码实例
class Solution {
public void moveZeroes(int[] nums) {
int left = 0, right = nums.length-1;
int temp;
while (left < right) {
while (nums[left++] != 0);
while (nums[right--] == 0);
temp = nums[right+1];
nums[right+1] = nums[left-1];
nums[left-1] = temp;
}
}
}
/************************************************/
class Solution {
public void moveZeroes(int[] nums) {
int pre = 0, post = 1;
int temp;
while (post < nums.length-1) {
while (nums[pre] != 0) pre++;
while (nums[post] == 0) post++;
temp = nums[pre];
nums[pre] = nums[post];
nums[post] = temp;
}
}
}
误区
- 在使用双指针时,循环边界条件确定有误,没有正确把握双指针的本质
- 未正确理清算法步骤情况下,盲目编程
心得总结
- 注意题干条件即问题特点
- 严格按照:理清问题-->确定算法步骤-->编写程序,这3个步骤进行解题.防止思维混乱.

浙公网安备 33010602011771号