解法一:统计零 + erase/remove
💡 解题思路
这道题要求把数组中的所有0移到末尾,同时保持非零元素原来的相对顺序。
第一种方法可以分成三步:
- 遍历数组,统计一共有多少个
0。 - 使用
remove()配合erase()删除数组中的所有0。 - 根据统计出的数量,在数组末尾补回相同数量的
0。
例如:
原数组:[0, 1, 0, 3, 12] 零的数量:2 删除所有零:[1, 3, 12] 末尾补两个零:[1, 3, 12, 0, 0]🧱 知识点卡片
🧹移动待保留元素
remove(nums.begin(), nums.end(), 0)会把所有非零元素向前移动,并返回新的“逻辑结尾”。它不会真正缩短
vector,末尾仍然保留着不再需要的元素,因此还要配合erase()。
✂️真正删除尾部区间
erase(new_end, nums.end())会真正删除从新逻辑结尾到原数组末尾的元素。把两步合在一起就是:
nums.erase(remove(nums.begin(), nums.end(), 0), nums.end());
➕在末尾添加元素
nums.push_back(0)会在vector的末尾添加一个0。循环执行
count次,就能补回之前删除的所有零,同时保持数组长度不变。
💻 代码实现
class Solution { public: void moveZeroes(vector<int>& nums) { int count = 0; // 统计数组中零的数量 for (int i = 0; i < nums.size(); ++i) { if (nums[i] == 0) { count++; } } // remove 把非零元素向前移动 // erase 真正删除末尾不再需要的区间 nums.erase( remove(nums.begin(), nums.end(), 0), nums.end() ); // 在数组末尾补回相同数量的零 for (int i = 0; i < count; ++i) { nums.push_back(0); } } };✅一句话总结:先数出零的数量,再删除所有零,最后把相同数量的零补到数组末尾。
解法二:双指针
💡 解题思路
使用两个位置:
i:从左到右扫描整个数组,寻找非零元素。next:指向下一个非零元素应该放置的位置。
当nums[i] != 0时,就交换nums[i]和nums[next],然后把next向右移动一位。
以[0, 1, 0, 3, 12]为例:
开始: [0, 1, 0, 3, 12] next = 0 遇到 1: [1, 0, 0, 3, 12] next = 1 遇到 3: [1, 3, 0, 0, 12] next = 2 遇到 12: [1, 3, 12, 0, 0] next = 3每次发现非零元素,就把它放到前面的正确位置。扫描结束后,所有非零元素都保持原顺序排列在前面,零自然被交换到末尾。
🧱 知识点卡片
👉next 指针
next表示“下一个非零元素应该放在哪里”。
next左边都是已经处理好的非零元素;只有成功放入一个非零元素后,next才会加 1。
🔄
swap()
swap(nums[i], nums[next])会交换当前位置和目标位置的元素。如果
i == next,相当于元素与自己交换,不会影响结果;因此代码不需要额外判断两个下标是否相同。
💻 代码实现
class Solution { public: void moveZeroes(vector<int>& nums) { // next 指向下一个非零元素应该放置的位置 int next = 0; // i 负责从左到右扫描整个数组 for (int i = 0; i < nums.size(); ++i) { if (nums[i] != 0) { // 把当前非零元素移动到前面的正确位置 swap(nums[i], nums[next]); // 下一个非零元素应该放到再右边一格 ++next; } } } };✅一句话总结:用
i寻找非零元素,用next标记它应该放置的位置;每找到一个非零元素就交换并移动next。
🆚 方法对比
⭐推荐使用双指针
解法一按照“统计、删除、补零”三个步骤完成,思路直观,也能保持非零元素的相对顺序。
解法二只需一次从左到右的扫描,通过交换原地完成移动,步骤更紧凑,也更符合这道题想考察的双指针思想。