283. 移动零

283 移动零

题目链接:283. 移动零

image-compressed.png

解题思路

非原地解(Silly Approach)

image-compressed.png

这种方法虽然直观,但由于创建了新数组,不符合题目“必须在原数组上操作”的要求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
if not nums:
return

# 这种方法创建了新数组,不符合原地操作的要求
res = []
for i in nums:
if i != 0:
res.append(i)

for i in range(len(nums) - len(res)):
res.append(0)

# 注意:需要切片赋值 nums[:] = res
# 直接 nums = res 只会改变局部引用,不会影响原始内存地址
nums[:] = res

原地解

方法一:两轮遍历

image-compressed.png

思路:使用两个指针。第一个指针用于记录当前已填充非零元素的位置,第二个指针负责遍历数组。

  1. 第一轮遍历:将所有非零元素按顺序移动到数组前端。
  2. 第二轮遍历:将剩余位置全部补零。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
if not nums:
return

# 慢指针 j 记录下一个非零元素应该存放的位置
j = 0
for i in range(len(nums)):
if nums[i] != 0:
nums[j] = nums[i]
j += 1

# 将剩余位置补零
for i in range(j, len(nums)):
nums[i] = 0

方法二:一轮遍历(快排思想)

image-compressed.png

利用快排(Partition)的思路:以 0 为基准,将非零元素交换到前面。这种方法只需一轮遍历。

1
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
if not nums:
return

# j 指向处理过的序列的尾部
j = 0
for i in range(len(nums)):
if nums[i] != 0:
# 交换非零元素到前面
nums[j], nums[i] = nums[i], nums[j]
j += 1

ACM 模式

本节提供 ACM 竞赛风格的完整可运行代码,包含输入输出处理。

输入格式:

1
num1 num2 num3 ... numN

输入一行整数数组,以空格分隔。

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import sys

class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
if not nums:
return

# 这种方法创建了新数组,不符合原地操作的要求
res = []
for i in nums:
if i != 0:
res.append(i)

for i in range(len(nums) - len(res)):
res.append(0)

# 注意:需要切片赋值 nums[:] = res
# 直接 nums = res 只会改变局部引用,不会影响原始内存地址
nums[:] = res

def main():
nums = list(map(int, sys.stdin.read().strip().split()))

sol = Solution()
result = sol.moveZeroes(nums)
print(result)

if __name__ == "__main__":
main()

运行示例:

1
echo "2 -1 3 4 -5" | python solution.py