1. 题目解析与核心思路
这道题目要求我们将数组中的所有0元素移动到末尾,同时保持非零元素的原始相对顺序。看似简单的要求背后,隐藏着几个关键约束:
- 原地操作:不能使用额外的数组空间,必须在原数组上直接修改
- 稳定性要求:非零元素必须保持原有的先后顺序
- 效率考量:最好能在O(n)时间复杂度和O(1)空间复杂度内完成
1.1 暴力解法的局限性
很多同学的第一反应可能是使用类似冒泡排序的方法,遇到0就与后面的元素交换,逐步将0"冒泡"到数组末尾。这种方法虽然可行,但存在明显缺陷:
- 时间复杂度高达O(n²)
- 需要进行大量不必要的元素交换
- 代码实现复杂且容易出错
1.2 双指针算法的优势
双指针算法之所以适合解决这类问题,是因为它能够:
- 分区管理:通过两个指针将数组划分为已处理和未处理区域
- 单次遍历:只需一次线性扫描即可完成操作
- 原地修改:通过元素交换实现位置调整,无需额外空间
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 双指针算法详解
2.1 指针定义与初始化
我们定义两个指针:
dest:指向已处理部分的最后一个非零元素cur:当前遍历位置的指针
初始化时:
cpp复制int dest = -1; // 表示尚未处理任何元素
int cur = 0; // 从第一个元素开始遍历
这种初始化方式清晰地表达了"尚未处理任何元素"的状态。当然,也可以将dest初始化为0,表示已处理非零区间的长度为0,两种方式本质相同。
2.2 核心算法流程
算法执行过程可以分为以下几个步骤:
- 遍历数组:
cur从0开始逐个检查数组元素 - 处理非零元素:
- 当
nums[cur] != 0时:dest先自增,扩展非零区间- 交换
nums[dest]和nums[cur]的值 cur自增继续遍历
- 当
- 处理零元素:
- 当
nums[cur] == 0时:- 直接跳过,
cur自增 - 零元素会自动留在"未处理"区域,最终会集中在数组末尾
- 直接跳过,
- 当
2.3 算法可视化示例
以数组`[0,1,0
