文章目录
-
-
- [**一、问题分析** 💡](#一、问题分析 💡)
- [**二、算法思路** 🚀](#二、算法思路 🚀)
- [**三、分步图解(以示例 `nums = [0,1,0,3,12]` 为例)** 📊](#三、分步图解(以示例
nums = [0,1,0,3,12]
为例) 📊) -
- [**1. 初始化指针**](#1. 初始化指针)
- [**2. 第一次循环(`cur=0`)**](#2. 第一次循环(
cur=0
)) - [**3. 第二次循环(`cur=1`)** 🎯](#3. 第二次循环(
cur=1
) 🎯) - [**4. 第三次循环(`cur=2`)**](#4. 第三次循环(
cur=2
)) - [**5. 第四次循环(`cur=3`)** 🔄](#5. 第四次循环(
cur=3
) 🔄) - [**6. 第五次循环(`cur=4`)** ✨](#6. 第五次循环(
cur=4
) ✨)
- [**四、代码解析** 🛠️](#四、代码解析 🛠️)
-
- [**关键点解释** 📌](#关键点解释 📌)
- [**五、总结** ✅](#五、总结 ✅)
-

一、问题分析 💡
题目要求将数组中的零元素全部移动到末尾,同时保持非零元素的原始顺序。关键约束是必须原地操作,即不能使用额外数组空间。这意味着我们需要在遍历过程中通过交换或覆盖元素实现目标。
二、算法思路 🚀
核心思想是使用双指针策略:
- 快指针
cur
:从左到右遍历数组,寻找非零元素。 - 慢指针
dest
:始终指向当前已处理好的非零序列的末尾下一个位置。
操作逻辑:
- 当
nums[cur]
非零时,将其与dest
位置交换,并让dest
后移。 - 最终,所有非零元素会被集中到数组前端,而
dest
之后的位置自然会被零填充。
三、分步图解(以示例 nums = [0,1,0,3,12]
为例) 📊
1. 初始化指针
-
dest = -1
(表示尚未找到非零元素) -
cur = 0
(从数组头部开始遍历)初始状态:
nums = [0, 1, 0, 3, 12]
↑
cur=0, dest=-1
2. 第一次循环(cur=0
)
-
nums[0]
是 0 → 不交换,cur
右移。状态不变:
nums = [0, 1, 0, 3, 12]
↑
cur=1, dest=-1
3. 第二次循环(cur=1
) 🎯
-
nums[1] = 1
非零 →dest
先自增到 0,交换nums[0]
和nums[1]
。交换后:
nums = [1, 0, 0, 3, 12]
↑
cur=1, dest=0 -
cur
右移,dest
更新为 0。
4. 第三次循环(cur=2
)
-
nums[2] = 0
→ 不交换,cur
右移。状态不变:
nums = [1, 0, 0, 3, 12]
↑
cur=3, dest=0
5. 第四次循环(cur=3
) 🔄
-
nums[3] = 3
非零 →dest
自增到 1,交换nums[1]
和nums[3]
。交换后:
nums = [1, 3, 0, 0, 12]
↑
cur=3, dest=1 -
cur
右移,dest
更新为 1。
6. 第五次循环(cur=4
) ✨
-
nums[4] = 12
非零 →dest
自增到 2,交换nums[2]
和nums[4]
。交换后:
nums = [1, 3, 12, 0, 0]
↑
cur=4, dest=2 -
遍历结束,所有非零元素已按序排列在前,后续位置自动补零。
四、代码解析 🛠️
cpp
class Solution {
public:
void moveZeroes(vector<int>& nums) {
for (int cur = 0, dest = -1; cur < nums.size(); ++cur) {
if (nums[cur]) { // 🎯 当前元素非零
swap(nums[++dest], nums[cur]); // 🔄 交换到非零序列末尾
}
}
}
};
关键点解释 📌
dest
的初始化:初始为 -1,表示尚未找到任何非零元素。- 交换条件 :仅当
nums[cur]
非零时,将其交换到dest
的下一个位置(即++dest
)。 - 时间复杂度:O(n) 🚀,仅一次遍历。
- 空间复杂度:O(1) 💾,原地操作。
五、总结 ✅
双指针法是解决此类问题的经典策略,通过一次遍历实现高效操作。核心在于维护两个指针的语义:
dest
始终指向已处理的非零序列末尾。cur
负责探索未处理区域,将非零元素按序交换到前方。