LeetCode 283:双指针技巧实现数组移动零

发布时间:2026/10/27 13:46:04
LeetCode 283:双指针技巧实现数组移动零 1. 题目解析与核心思路283.移动零是LeetCode Hot100中的经典题目题目要求将数组中的所有0移动到末尾同时保持非零元素的相对顺序。看似简单但考察了数组操作的基本功和双指针技巧的灵活运用。这道题的难点在于如何在O(n)时间复杂度和O(1)空间复杂度下完成操作。直接想到的暴力解法是遇到0就删除并在末尾添加但这样会导致大量元素移动时间复杂度会达到O(n²)。更优的解法是使用双指针技巧这也是面试官最希望看到的解决方案。2. 双指针解法详解2.1 快慢指针实现最标准的解法是使用快慢双指针初始化慢指针slow0快指针fast从0开始遍历数组当nums[fast]≠0时将其赋值给nums[slow]然后slow遍历结束后将slow之后的所有元素置为0这种解法保证了时间复杂度O(n)只需遍历数组一次空间复杂度O(1)没有使用额外空间def moveZeroes(nums): slow 0 for fast in range(len(nums)): if nums[fast] ! 0: nums[slow] nums[fast] slow 1 for i in range(slow, len(nums)): nums[i] 02.2 优化版双指针上述解法需要第二次遍历来填充0可以进一步优化为单次遍历完成def moveZeroes(nums): slow 0 for fast in range(len(nums)): if nums[fast] ! 0: nums[slow], nums[fast] nums[fast], nums[slow] slow 1这个版本通过交换元素实现当fast遇到非0元素时就与slow交换这样slow左侧都是非0元素右侧自然就是0。3. 边界条件与测试用例3.1 常见边界情况全0数组[0,0,0] → [0,0,0]无0数组[1,2,3] → [1,2,3]单元素数组[0] → [0][1] → [1]0在开头[0,1,2] → [1,2,0]0在结尾[1,2,0] → [1,2,0]连续0[1,0,0,2,3] → [1,2,3,0,0]3.2 测试代码示例def test_moveZeroes(): test_cases [ ([0,1,0,3,12], [1,3,12,0,0]), ([0], [0]), ([1,2,3], [1,2,3]), ([0,0,1], [1,0,0]), ([1,0,2,0,3], [1,2,3,0,0]) ] for input, expected in test_cases: moveZeroes(input) assert input expected, fFailed: {input} ! {expected} print(All test cases passed!)4. 算法复杂度分析4.1 时间复杂度两种双指针解法都是O(n)基本版第一次遍历n个元素第二次最多遍历n个元素 → O(2n) O(n)优化版仅一次遍历n个元素 → O(n)4.2 空间复杂度两种解法都是O(1)只使用了固定数量的额外变量(slow,fast等)没有使用与输入规模相关的额外空间5. 同类题目拓展掌握移动零后可以解决一系列类似问题5.1 移除指定元素(LeetCode 27)def removeElement(nums, val): slow 0 for fast in range(len(nums)): if nums[fast] ! val: nums[slow] nums[fast] slow 1 return slow5.2 去重(LeetCode 26)def removeDuplicates(nums): if not nums: return 0 slow 1 for fast in range(1, len(nums)): if nums[fast] ! nums[slow-1]: nums[slow] nums[fast] slow 1 return slow5.3 颜色分类(LeetCode 75)def sortColors(nums): p0, curr, p2 0, 0, len(nums)-1 while curr p2: if nums[curr] 0: nums[p0], nums[curr] nums[curr], nums[p0] p0 1 curr 1 elif nums[curr] 2: nums[curr], nums[p2] nums[p2], nums[curr] p2 - 1 else: curr 16. 面试技巧与注意事项6.1 面试常见问题能否解释你的解法思路为什么选择双指针而不是其他方法如何处理边界条件时间/空间复杂度是多少如何测试你的代码6.2 回答建议先明确问题要求保持顺序、原地修改解释双指针工作原理slow记录非0位置fast寻找非0元素强调复杂度优势O(n)时间O(1)空间主动提及边界条件和测试用例6.3 常见错误新建数组返回不符合原地修改要求使用remove和append时间复杂度高忽略保持顺序的要求交换逻辑错误导致顺序改变7. 刷题建议与学习路径7.1 针对数组类题目先掌握双指针技巧快慢指针、左右指针学习排序算法快速排序、归并排序练习二分查找变种掌握滑动窗口技巧7.2 LeetCode学习路线初级数组基本操作 → 双指针 → 二分查找中级滑动窗口 → 前缀和 → 二维数组高级单调栈 → 堆的应用 → 分治算法7.3 每日刷题计划每天1-2道Easy题巩固基础每周3-4道Medium题提升技巧每月2-3道Hard题挑战思维定期复习Hot100和剑指Offer8. 实际工程应用虽然看似简单但移动零的思想在实际开发中广泛应用8.1 数据处理场景过滤无效数据如日志中的空记录压缩稀疏数组数据库记录整理8.2 性能优化案例// 优化前使用List.remove() O(n²) ListInteger list new ArrayList(); // ...添加元素... list.removeIf(x - x 0); // 优化后双指针 O(n) int slow 0; for (int fast 0; fast list.size(); fast) { if (list.get(fast) ! 0) { list.set(slow, list.get(fast)); } } while (slow list.size()) { list.set(slow, 0); }8.3 系统设计应用消息队列中过滤无效消息缓存淘汰策略实现资源整理与碎片整理算法9. 不同语言实现对比9.1 Java实现public void moveZeroes(int[] nums) { int slow 0; for (int fast 0; fast nums.length; fast) { if (nums[fast] ! 0) { int temp nums[slow]; nums[slow] nums[fast]; nums[fast] temp; slow; } } }9.2 C实现void moveZeroes(vectorint nums) { for (int slow 0, fast 0; fast nums.size(); fast) { if (nums[fast] ! 0) { swap(nums[slow], nums[fast]); } } }9.3 JavaScript实现function moveZeroes(nums) { let slow 0; for (let fast 0; fast nums.length; fast) { if (nums[fast] ! 0) { [nums[slow], nums[fast]] [nums[fast], nums[slow]]; slow; } } }9.4 Go实现func moveZeroes(nums []int) { slow : 0 for fast : 0; fast len(nums); fast { if nums[fast] ! 0 { nums[slow], nums[fast] nums[fast], nums[slow] slow } } }10. 进阶思考与挑战10.1 如果要求保持非零元素的原始顺序标准解法已经满足但如果是更复杂的交换条件可能需要记录原始位置。10.2 如果要求最小化写操作次数def moveZeroesMinWrite(nums): slow 0 for fast in range(len(nums)): if nums[fast] ! 0: if slow ! fast: # 避免不必要的写操作 nums[slow] nums[fast] slow 1 for i in range(slow, len(nums)): nums[i] 010.3 如果数组很大且零很少可以考虑只记录非零位置最后统一处理减少写操作def moveZeroesSparse(nums): non_zeros [] for i in range(len(nums)): if nums[i] ! 0: non_zeros.append(i) for j in range(len(non_zeros)): if j ! non_zeros[j]: nums[j] nums[non_zeros[j]] for k in range(len(non_zeros), len(nums)): nums[k] 0在实际面试中写完基本解法后可以主动与面试官讨论这些优化方向展示你的思考深度。