文章目录
-
-
- [**一、问题分析** 💡](#一、问题分析 💡)
- [**二、算法思路** 🚀](#二、算法思路 🚀)
- [**三、分步图解(以示例 `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负责探索未处理区域,将非零元素按序交换到前方。