文章目录
-
- 一、题目描述
- 二、题意总结
- 三、常见解法分析
- 解法二:记录位置法(非交换优化版本)
- [🧮 多方法对比](#🧮 多方法对比)
- 四、方法演化图(Mermaid)
- 总结
一、题目描述
给定一个数组
nums,将所有的0移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
text
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
🧠 要求:
- 必须在**原地(in-place)**进行操作;
- 尽量减少操作次数。
二、题意总结
就是让数组中非零元素保持顺序不变的前提下,把所有 0 挪到末尾。
即实现 "非零元素 ➜ 前面" + "0 元素 ➜ 后面"
三、常见解法分析
✅ 解法一:双指针法(最优方案)
核心思想:
- 用一个指针
slow记录下一个非零元素应该放的位置; - 另一个指针
fast遍历数组。 - 当
nums[fast] != 0时,把它移动到slow的位置。 - 遍历结束后,
slow之前全是非零,slow之后全部设为 0。
流程图
nums[fast]!=0
nums[fast]==0
开始
初始化 slow=0
for fast 从 0 到 n-1
交换 nums[slow] 和 nums[fast]
slow++
跳过 fast++
遍历结束
输出结果
例子演示
以 nums = [0,1,0,3,12] 为例:
| 步骤 | fast | slow | nums | 说明 |
|---|---|---|---|---|
| 初始 | - | 0 | [0,1,0,3,12] |
开始 |
| 1 | 0 | 0 | [0,1,0,3,12] |
0,跳过 |
| 2 | 1 | 0 → 1 | [1,0,0,3,12] |
交换 1 与 0 |
| 3 | 2 | 1 | [1,0,0,3,12] |
0,跳过 |
| 4 | 3 | 1 → 2 | [1,3,0,0,12] |
交换 3 与 0 |
| 5 | 4 | 2 → 3 | [1,3,12,0,0] |
交换 12 与 0 |
最终结果:[1,3,12,0,0] ✅
Java 实现(推荐)
java
public class Solution {
public void moveZeroes(int[] nums) {
int slow = 0;
for (int fast = 0; fast < nums.length; fast++) {
if (nums[fast] != 0) {
// 交换非零元素和当前位置 slow 的数
int temp = nums[slow];
nums[slow] = nums[fast];
nums[fast] = temp;
slow++;
}
}
}
}
复杂度分析
| 项目 | 复杂度 |
|---|---|
| 时间复杂度 | O(n):只遍历一次 |
| 空间复杂度 | O(1):原地操作 |
解法二:记录位置法(非交换优化版本)
思路:
- 用一个指针
pos指示下一个要放非零元素的位置; - 一次遍历,将所有非 0 覆盖到前面;
- 再将剩下的地方补 0。
java
public class Solution {
public void moveZeroes(int[] nums) {
int pos = 0;
for (int num : nums) {
if (num != 0) {
nums[pos++] = num;
}
}
while (pos < nums.length) {
nums[pos++] = 0;
}
}
}
🔸 优点:
- 更"干净"的写法,无需每次 swap。
- 缺点:比双指针交换多一次遍历,但仍是 O(n)。
🧮 多方法对比
| 解法 | 关键思路 | 时间复杂度 | 空间复杂度 | 是否原地 | 稳定性 |
|---|---|---|---|---|---|
| 双指针 swap | 快慢指针交换位置 | O(n) | O(1) | ✅ | ✅ |
| 覆盖法 | 一次赋值一次补零 | O(n) | O(1) | ✅ | ✅ |
| 新数组 | 借助中间数组重建 | O(n) | O(n) | ❌ | ✅ |
四、方法演化图(Mermaid)
暴力法:新数组重建
优化:记录非零位置
最终方案:双指针交换法 O(n)
总结
| 要点 | 说明 |
|---|---|
| 🎯 目标 | 把 0 全部移到末尾,非 0 保持顺序 |
| 💡 核心 | 快慢指针 + 交换 或 赋值法 |
| 🧮 时间复杂度 | O(n) |
| 🧩 空间复杂度 | O(1) |
| ✅ 最优解 | 双指针交换法 |