(每日一题) 力扣 283 移动零
文章目录
- **一、问题分析** 💡
- **二、算法思路** 🚀
- **三、分步图解(以示例 `nums = [0,1,0,3,12]` 为例)** 📊
- **1. 初始化指针**
- **2. 第一次循环(`cur=0`)**
- **3. 第二次循环(`cur=1`)** 🎯
- **4. 第三次循环(`cur=2`)**
- **5. 第四次循环(`cur=3`)** 🔄
- **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
- 遍历结束,所有非零元素已按序排列在前,后续位置自动补零。
四、代码解析 🛠️
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
负责探索未处理区域,将非零元素按序交换到前方。