一、前言
在算法面试和编程学习中,++双指针++ 是一种非常高效且常用的技巧。它通过两个下标(或指针)在数组(或链表)上进行移动,从而在 O(N)的时间复杂度内解决许多看似复杂的问题。今天,我将通过 ++LeetCode 283. 移动零++这道题,来深入理解双指针算法的核心思想和应用。
二、题目描述
283. 移动零
给定一个数组
nums,编写一个函数将所有0移动到数组的末尾,同时保持非零元素的相对顺序。请注意,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例 2:
输入: nums = [0]
输出: [0]
三、解题思路:双指针算法
3.1 核心思想:数组划分 + 区间管理
这道题的本质是 对数组进行原地划分,将数组分为两个部分:
-
非零元素区:保持原有相对顺序。
-
零元素区:全部移动到数组末尾。
为了实现这一点,我们可以使用 双指针:
-
cur:当前遍历指针,从左往右扫描数组。 -
dest:已处理的非零元素的最后一个位置(即非零区的末尾)。
3.2 三个区间的划分
在任意时刻,数组可以被划分为三个区间:
-
[0, dest]:已处理的非零元素区。 -
[dest + 1, cur - 1]:已处理的零元素区(这部分其实可以忽略,因为我们不关心零的顺序)。 -
[cur, n - 1]:待处理区。
✅ 关键点 :
dest始终指向当前已处理的非零元素的最后一个位置,cur负责探索未知区域。

3.3 指针的移动逻辑
-
当
nums[cur] != 0时:-
将
nums[cur]与nums[dest + 1]交换(把非零元素放到非零区的末尾)。 -
dest++(扩展非零区)。 -
cur++(继续探索)。
-
-
当
nums[cur] == 0时:- 只移动
cur++(跳过零,等待后续非零元素来交换)。
- 只移动
💡 为什么可以交换?
因为
dest + 1的位置要么是零(可以直接覆盖),要么是非零(但此时cur指向的也是非零,交换后不影响相对顺序)。
四、代码实现(Java)
java
class Solution {
public void moveZeroes(int[] nums) {
int cur = 0, dest = -1; // dest 初始为 -1,表示非零区为空
for (; cur < nums.length; cur++) {
if (nums[cur] != 0) {
dest++;
// 交换 nums[cur] 和 nums[dest]
int tmp = nums[cur];
nums[cur] = nums[dest];
nums[dest] = tmp;
}
}
}
}
✅ 代码亮点:
使用
dest = -1初始化,简化边界判断。一次遍历完成,时间复杂度 O(N),空间复杂度 O(1)。
五、算法可视化(手绘图解)
我们用手写图示来辅助理解:
初始状态:
[0, 1, 0, 3, 12]
cur=0, dest=-1
步骤1:cur=0,nums[0]=0 → 跳过,cur=1
步骤2:cur=1,nums[1]=1 ≠0 → dest=0,交换 nums[1] 和 nums[0]
[1, 0, 0, 3, 12]
步骤3:cur=2,nums[2]=0 → 跳过,cur=3
步骤4:cur=3,nums[3]=3 ≠0 → dest=1,交换 nums[3] 和 nums[1]
[1, 3, 0, 0, 12]
步骤5:cur=4,nums[4]=12 ≠0 → dest=2,交换 nums[4] 和 nums[2]
[1, 3, 12, 0, 0]
结束。
🎯 最终效果:非零元素保持相对顺序,零元素全部移到末尾。
六、复杂度分析
-
时间复杂度:O(N),其中 N是数组长度。我们只遍历了一次数组。
-
空间复杂度:O(1),只使用了常数个额外变量。
七、拓展思考:与快速排序的联系
在快速排序中,也有类似"分区"的思想:
-
选择一个基准值
tmp。 -
将小于
tmp的放左边,大于tmp的放右边。
移动零 可以看作是 快速排序分区的一个特例:
-
基准值
tmp = 0。 -
小于
tmp的(即非零元素)放左边。 -
大于
tmp的(即零元素)放右边。
✅ 启示:很多算法题的核心思想是相互关联的,掌握底层逻辑可以举一反三。
八、总结
通过 LeetCode 283. 移动零 这道题,我深入学习了:
-
双指针算法 的基本思想和应用场景。
-
数组划分 和 区间管理 的技巧。
-
原地操作 的优化策略。
-
与 快速排序 的关联,提升算法思维广度。
双指针是算法面试中的常客,掌握它不仅有助于解题,更能培养我们对数据结构的深刻理解和灵活运用能力。
九、参考资料
- LeetCode 283. Move Zeroes: https://leetcode.cn/problems/move-zeroes/
✅ 下期预告:我将继续讲解双指针的其他经典题型,如"两数之和 II"、"盛最多水的容器"等,敬请期待!
📌 **关注我,一起进阶算法之路!**
算法 #双指针 #LeetCode #Java #移动零 #编程学习 #优选算法专栏