主题
方法一:把 nums 当作栈
用一个栈记录非零元素。
看示例 1,
| 栈 | |||
|---|---|---|---|
| 否 | |||
| 是 | |||
| 否 | |||
| 是 | |||
| 是 |
最后,在栈的末尾添加两个
为了做到
入栈就是把
最后把
python
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
stack_size = 0
for x in nums:
if x:
nums[stack_size] = x # 把 x 入栈
stack_size += 1
for i in range(stack_size, len(nums)):
nums[i] = 0cpp
// C++ 版待补充python
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
stack_size = 0
for x in nums:
if x:
nums[stack_size] = x # 把 x 入栈
stack_size += 1
nums[stack_size:] = [0] * (len(nums) - stack_size)cpp
// C++ 版待补充cpp
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int stack_size = 0;
for (int x : nums) {
if (x) {
nums[stack_size++] = x; // 把 x 入栈
}
}
fill(nums.begin() + stack_size, nums.end(), 0);
}
};复杂度分析
- 时间复杂度:
,其中 是 的长度。 - 空间复杂度:
。
方法二:双指针+交换元素
方法一在最坏情况下(
核心思路
把
例如
为了保证非零元素的顺序不变,我们需要维护最左边的空位的位置(下标)。
具体思路
从左到右遍历
同时维护另一个下标
每次遇到
如果
示例 1 的
| 操作后 | |||
|---|---|---|---|
| 不操作 | |||
| 不操作 | |||
由于每次操作后,
答疑
问:如果
答:
python
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
循环不变量:在循环过程中,nums 的数据分布始终如下图
[ 非零元素 | 零元素 | 尚未遍历 ]
^ ^
start0 i
"""
start0 = 0
for i in range(len(nums)):
if nums[i]:
nums[i], nums[start0] = nums[start0], nums[i]
start0 += 1cpp
// C++ 版待补充cpp
class Solution {
public:
void moveZeroes(vector<int>& nums) {
/*
循环不变量:在循环过程中,nums 的数据分布始终如下图
[ 非零元素 | 零元素 | 尚未遍历 ]
^ ^
start0 i
*/
int start0 = 0;
for (int& x : nums) { // 注意 x 是引用
if (x) {
swap(x, nums[start0]);
start0++;
}
}
}
};复杂度分析
- 时间复杂度:
,其中 是 的长度。 - 空间复杂度:
。
专题训练
见下面双指针题单的「§3.4 原地修改」。
分类题单
- 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
- 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
- 单调栈(基础/矩形面积/贡献法/最小字典序)
- 网格图(DFS/BFS/综合应用)
- 位运算(基础/性质/拆位/试填/恒等式/思维)
- 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
- 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
- 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
- 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
- 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
- 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
- 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)
欢迎关注 B站@灵茶山艾府