🔥个人主页: Milestone-里程碑
❄️个人专栏: <<力扣hot100>> <<C++>><<Linux>>
🌟心向往之行必能至
题目描述
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。注意:必须在不复制数组的情况下原地对数组进行操作。
示例
- 输入:
nums = [0,1,0,3,12] - 输出:
[1,3,12,0,0]
方法一:双指针交换法
思路
我们可以用一个指针 left 来记录当前非零元素应该放置的位置,然后遍历数组。当遇到非零元素时,就将它与 left 位置的元素交换,并让 left 指针右移。这样可以保证 left 指针之前的所有元素都是非零的,且保持了原有顺序。
代码实现
cpp
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int left = 0;
for (auto& x : nums) {
if (x != 0) {
swap(x, nums[left]);
left++;
}
}
}
};
复杂度分析
- 时间复杂度:O (n),其中 n 是数组的长度。我们只需要遍历一次数组。
- 空间复杂度:O (1),只使用了常数级的额外空间。
方法二:覆盖补零法
思路
- 先遍历数组,把所有非零元素依次覆盖到数组的前面,用一个变量
stack_size记录当前覆盖的位置。 - 遍历完成后,
stack_size及之后的位置全部补零即可。
代码实现
cpp
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int stack_size = 0;
// 第一步:把非零元素移到前面
for (int x : nums) {
if (x != 0) {
nums[stack_size++] = x;
}
}
// 第二步:后面的位置全部补零
while (stack_size < nums.size()) {
nums[stack_size++] = 0;
}
}
};
复杂度分析
- 时间复杂度:O (n),两次遍历数组,总时间还是线性的。
- 空间复杂度:O (1),同样是原地操作。
方法对比
| 方法 | 优点 | 缺点 |
|---|---|---|
| 双指针交换法 | 只需一次遍历,操作次数更少 | 交换操作会多写几次赋值 |
| 覆盖补零法 | 逻辑直观,容易理解 | 需要两次遍历,操作次数略多 |
在进阶要求 "尽量减少操作次数" 时,双指针交换法会更有优势。
总结
这道题的核心是原地修改 和保持相对顺序,两种方法都能满足要求。