题目:
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例 2:
输入: nums = [0]
输出: [0]
核心思路:双指针覆盖法
关键洞察 💡
把问题拆解成两步:
不需要交换,直接覆盖!
思维模型
想象你在整理书架:
<span style="color:#abb2bf"><code>原始书架:[空, 书A, 空, 书B, 书C]
步骤1:把所有书依次往前移
发现 书A → 放到位置0
发现 书B → 放到位置1
发现 书C → 放到位置2
中间状态:[书A, 书B, 书C, 书B, 书C]
↑ 后面还有旧数据
步骤2:把后面的位置清空
位置3 → 空
位置4 → 空
最终书架:[书A, 书B, 书C, 空, 空] ✅</code></span>
双指针的含义
java
<span style="color:#abb2bf"><code class="language-java"><span style="color:#c678dd">int</span> slow <span style="color:#61afef">=</span> <span style="color:#d19a66">0</span><span style="color:#abb2bf">;</span> <span style="color:#5c6370"><em>// "慢指针" – 下一个非零元素应该放的位置</em></span>
<span style="color:#c678dd">int</span> fast <span style="color:#61afef">=</span> <span style="color:#d19a66">0</span><span style="color:#abb2bf">;</span> <span style="color:#5c6370"><em>// "快指针" – 遍历数组的指针</em></span></code></span>
指针职责
| slow | 标记"非零区域"的边界 | 只在放入非零元素后移动 |
| fast | 遍历查找非零元素 | 每次循环都移动 |
详细执行过程
输入:[0, 1, 0, 3, 12]
第一阶段:收集非零元素
<span style="color:#abb2bf"><code>初始状态:
数组:[0, 1, 0, 3, 12]
↑
slow=0
fast=0
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Step 1: fast=0
nums[0]=0 → 是零,跳过
slow 不动
数组:[0, 1, 0, 3, 12]
↑
slow=0
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Step 2: fast=1
nums[1]=1 → 非零!
执行:nums[slow] = nums[fast]
nums[0] = 1
slow++
数组:[1, 1, 0, 3, 12]
↑
slow=1
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Step 3: fast=2
nums[2]=0 → 是零,跳过
slow 不动
数组:[1, 1, 0, 3, 12]
↑
slow=1
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Step 4: fast=3
nums[3]=3 → 非零!
执行:nums[slow] = nums[fast]
nums[1] = 3
slow++
数组:[1, 3, 0, 3, 12]
↑
slow=2
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Step 5: fast=4
nums[4]=12 → 非零!
执行:nums[slow] = nums[fast]
nums[2] = 12
slow++
数组:[1, 3, 12, 3, 12]
↑
slow=3
第一阶段结束!
非零元素已收集完毕,slow=3 表示后面需要填零</code></span>
第二阶段:填充零
<span style="color:#abb2bf"><code>从 slow=3 开始,到数组末尾全部填 0
i=3: nums[3]=0
数组:[1, 3, 12, 0, 12]
i=4: nums[4]=0
数组:[1, 3, 12, 0, 0]
完成!✅</code></span>
题解
class Solution {
public void moveZeroes(int[] nums) {
int slow = 0; // 指向下一个非零元素应该放的位置
// 步骤1:将所有非零元素依次移到前面
for (int fast = 0; fast < nums.length; fast++) {
if (nums[fast] != 0) {
nums[slow] = nums[fast];
slow++;
}
}
// 步骤2:将剩余位置填充为 0
for (int i = slow; i < nums.length; i++) {
nums[i] = 0;
}
}
}
```
**演示过程**:
```
输入:[0, 1, 0, 3, 12]
步骤1:移动非零元素
fast=0, nums[0]=0 → 跳过,slow=0
fast=1, nums[1]=1 → nums[0]=1, slow=1
fast=2, nums[2]=0 → 跳过,slow=1
fast=3, nums[3]=3 → nums[1]=3, slow=2
fast=4, nums[4]=12 → nums[2]=12, slow=3
数组变为:[1, 3, 12, 3, 12]
slow=3 ↑
步骤2:填充零
i=3: nums[3]=0
i=4: nums[4]=0
最终:[1, 3, 12, 0, 0] ✅



