Article / 文章
LeetCode 第283题:移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 请注意: 1. 必须在原数组上操作,不能拷贝额外的数组。 2. 尽量减少操作次数。
📖 文章摘要
本文详细解析LeetCode第283题“移动零”,这是一道考察数组操作的简单题目。文章提供了双指针和单次遍历两种实现方案,包含C#、Python、C++三种语言实现,配有详细的指针移动分析和性能对比。适合初学者学习数组操作和指针技巧。
核心知识点: 数组操作、双指针技巧、原地算法
难度等级: 简单
推荐人群: 算法初学者,想要掌握基本数组操作的开发者
题目描述
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意:
- 必须在原数组上操作,不能拷贝额外的数组。
- 尽量减少操作次数。
示例
示例 1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例 2:
输入: nums = [0]
输出: [0]
提示
1 <= nums.length <= 10^4-2^31 <= nums[i] <= 2^31 - 1
解题思路
本题可以使用两种方法来实现:
-
双指针法:
- 使用两个指针,一个用于遍历数组,一个用于记录非零元素的位置
- 遇到非零元素时,将其移动到前面的位置
- 最后将剩余位置填充为0
-
单次遍历法:
- 遍历数组,记录非零元素的个数
- 将非零元素依次前移
- 将剩余位置填充为0
图解思路
双指针移动过程分析表
| 步骤 | 数组状态 | 慢指针 | 快指针 | 操作 |
|---|---|---|---|---|
| 初始 | [0,1,0,3,12] | 0 | 0 | 开始 |
| 1 | [0,1,0,3,12] | 0 | 1 | 找到1 |
| 2 | [1,0,0,3,12] | 1 | 2 | 交换0和1 |
| 3 | [1,0,0,3,12] | 1 | 3 | 找到3 |
| 4 | [1,3,0,0,12] | 2 | 4 | 交换0和3 |
| 5 | [1,3,12,0,0] | 3 | 5 | 交换0和12 |
操作步骤分析表
| 操作 | 目的 | 时间复杂度 | 空间复杂度 |
|---|---|---|---|
| 遍历数组 | 找到非零元素 | O(n) | O(1) |
| 交换元素 | 保持相对顺序 | O(1) | O(1) |
| 填充零 | 完成最终结果 | O(1) | O(1) |
代码实现
C# 实现
public class Solution {
public void MoveZeroes(int[] nums) {
int lastNonZeroFoundAt = 0;
// 将所有非零元素移到前面
for (int i = 0; i < nums.Length; i++) {
if (nums[i] != 0) {
nums[lastNonZeroFoundAt++] = nums[i];
}
}
// 将剩余位置填充为0
for (int i = lastNonZeroFoundAt; i < nums.Length; i++) {
nums[i] = 0;
}
}
}
Python 实现
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
lastNonZeroFoundAt = 0
# 将所有非零元素移到前面
for i in range(len(nums)):
if nums[i] != 0:
nums[lastNonZeroFoundAt] = nums[i]
lastNonZeroFoundAt += 1
# 将剩余位置填充为0
for i in range(lastNonZeroFoundAt, len(nums)):
nums[i] = 0
C++ 实现
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int lastNonZeroFoundAt = 0;
// 将所有非零元素移到前面
for (int i = 0; i < nums.size(); i++) {
if (nums[i] != 0) {
nums[lastNonZeroFoundAt++] = nums[i];
}
}
// 将剩余位置填充为0
for (int i = lastNonZeroFoundAt; i < nums.size(); i++) {
nums[i] = 0;
}
}
};
执行结果
C# 实现
- 执行用时:160 ms
- 内存消耗:42.8 MB
Python 实现
- 执行用时:44 ms
- 内存消耗:15.7 MB
C++ 实现
- 执行用时:16 ms
- 内存消耗:19.1 MB
性能对比
| 语言 | 执行用时 | 内存消耗 | 特点 |
|---|---|---|---|
| C# | 160 ms | 42.8 MB | 代码结构清晰,性能适中 |
| Python | 44 ms | 15.7 MB | 代码最简洁,性能不错 |
| C++ | 16 ms | 19.1 MB | 性能最优,内存占用适中 |
代码亮点
- 🎯 使用双指针技巧实现原地操作
- 💡 最小化操作次数,提高效率
- 🔍 无需额外空间,空间复杂度O(1)
- 🎨 代码简洁易懂,易于维护
常见错误分析
- 🚫 使用额外数组存储
- 🚫 未保持非零元素相对顺序
- 🚫 操作次数过多
- 🚫 边界条件处理不当
解法对比
| 解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| 双指针法 | O(n) | O(1) | 操作次数少 | 需要两次遍历 |
| 单次遍历法 | O(n) | O(1) | 只需一次遍历 | 实现略复杂 |
| 冒泡排序法 | O(n^2) | O(1) | 实现简单 | 效率低下 |
相关题目
- LeetCode 27. 移除元素 - 简单
- LeetCode 26. 删除有序数组中的重复项 - 简单
- LeetCode 80. 删除有序数组中的重复项 II - 中等
📖 系列导航
🔥 算法专题合集 - 查看完整合集
📢 关注合集更新:点击上方合集链接,关注获取最新题解!目前已更新第283题。
💬 互动交流
感谢大家耐心阅读到这里!希望这篇题解能够帮助你更好地理解和掌握这道算法题。
如果这篇文章对你有帮助,请:
- 👍 点个赞,让更多人看到这篇文章
- 📁 收藏文章,方便后续查阅复习
- 🔔 关注作者,获取更多高质量算法题解
- 💭 评论区留言,分享你的解题思路或提出疑问
你的支持是我持续分享的动力!
💡 一起进步:算法学习路上不孤单,欢迎一起交流学习!