ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

LeetCode 283:移动零(双指针问题) —— 题解

LeetCode 283:移动零(双指针问题) —— 题解 欢迎阅读一.题目283. 移动零 - 力扣LeetCode​ 欢迎来到「移动零」题解之旅本文将带你从“将所有零移到数组末尾同时保持非零元素顺序”这一数组操作问题出发深入理解双指针快慢指针的经典应用并掌握如何原地修改数组实现高效的一次遍历。在开始之前建议你先了解题目背景这是 LeetCode 283 题给定一个数组nums要求将所有0移动到数组末尾并保持非零元素的相对顺序不变且必须原地操作不能复制新数组。这是数组操作中的基础题也是双指针思想的入门经典。明确学习目标掌握双指针解法——维护一个“慢指针”指向已处理好的非零序列的末尾用“快指针”遍历数组遇到非零元素则交换或覆盖到慢指针位置最后将剩余位置填零。理解为什么这种“只关心非零元素遇到零就跳过”的策略能保持相对顺序并熟练处理边界如全零数组或全非零数组。准备好环境建议在本地 IDE 或 LeetCode 在线编辑器中打开代码边看边运行亲手验证示例如nums [0,1,0,3,12]输出[1,3,12,0,0]。本文将从问题转化、双指针策略设计快慢指针详解、代码模拟到复杂度分析层层递进。即使你对双指针还不熟悉我们也会从“用慢指针记录非零元素应该放的位置”这一直觉出发让你轻松抓住核心思想——快指针负责探路慢指针负责记录所有非零元素依次往前靠零自然被挤到后面。现在让我们一起把零“搬运”到末尾让数组焕然一新吧 二.做题思路一、问题分析前置分析给定一个数组nums要求将所有 0 移动到数组末尾同时保持非零元素的相对顺序不变。必须原地修改不能复制数组。核心观察等价于将所有非零元素按原顺序压缩到数组前端剩余位置全部填充 0。二、算法策略双指针使用快指针fast遍历数组慢指针slow指向下一个非零元素应该存放的位置。初始化slow 0。遍历过程若nums[fast] ! 0则将nums[fast]赋值给nums[slow]然后slow。无论当前元素是否为 0fast都向后移动。遍历结束后slow之前的位置都已放置非零元素将slow到末尾的所有位置置为 0。三、正确性说明简单版本快慢指针保证了所有非零元素按原顺序被依次“搬运”到数组前部且不会丢失任何非零元素。因为slow始终指向下一个可放置非零元素的位置而fast负责遍历所有元素遇到非零就覆盖到slow处。最后将剩余位置置零既保留了非零顺序又确保了所有 0 都在末尾。该算法一次遍历即可完成正确性由指针移动逻辑保证。四、实现细节边界防护若数组长度n 1直接返回无需操作。使用int slow 0。for (int fast 0; fast n; fast)遍历若nums[fast] ! 0则nums[slow] nums[fast]。遍历结束后从slow到n-1循环赋值0。时间复杂度 O(n)空间复杂度 O(1)满足原地要求。五、返回值目标映射不需要返回值原地修改数组使所有 0 移动到末尾非零元素相对顺序不变。三.代码class Solution { public: void moveZeroes(vectorint nums) { // 算法思路双指针法 // left 指向当前可能存放非零元素的位置也是等待被非零元素替换的位置 // right 从 left1 开始向后查找非零元素一旦找到就与 left 交换 // 然后将 left 右移一位继续处理。 // 这样就能保证所有非零元素按原顺序前移所有零被移动到末尾。 int left 0; int right left 1; int n nums.size(); // 当右指针未越界时持续扫描 while (right n) { // 如果左指针指向0说明此处需要被非零元素替换 if (nums[left] 0) { // 如果右指针指向非零元素则交换将非零元素移到左指针位置 if (nums[right] ! 0) { swap(nums[left], nums[right]); // 注意交换后left 位置变为非零但 left 并未自增 // 下一次循环时会进入 else 分支将 left 和 right 都右移 // 相当于 left 指向了下一位right 指向下下位正确。 } else { // 如果右指针也指向0则右指针继续右移寻找非零元素 right; } } else { // 如果左指针指向非零说明当前位置已经正确将两个指针同时右移 left; right; } } } };四、流程图 闭幕 恭喜你完成了「移动零」问题的学习为了巩固知识并进一步拓展建议你动手实践在 LeetCode 上提交代码尝试不同的测试用例。深入思考本题要求原地移动零且保持非零元素的相对顺序。常用的解法是双指针快慢指针slow指向已处理区域的末尾fast用于遍历。请问slow和fast各自的具体职责是什么如果采用覆盖法先移非零再补零与交换法遇非零即与slow交换两种方式的操作次数有何差异哪种在时间复杂度相同的情况下更高效若数组包含负数题目只要求移动零负数应视为非零元素保持顺序算法逻辑是否需要改动如果要求将所有零移动到数组开头而非末尾你只需修改判断条件中的哪一处请动手试一试。本题强制不复制数组若允许复制你会用怎样的额外空间方案实现此时的时间复杂度是否变化延伸挑战若问题改为将指定值不限于 0全部移到末尾且保持其他元素顺序你的代码应做哪些通用化改造如果你觉得本文对你有所帮助欢迎点赞 / 收藏关注作者获取更多题解留言交流你的疑问或优化思路✅深入思考答案双指针职责slow表示已排好的非零区域的下一个位置即慢指针fast用于遍历数组寻找非零元素。覆盖法 vs 交换法两者时间复杂度均为 O(n)但覆盖法只需赋值非零前移 末尾补零交换法需三次赋值交换因此覆盖法常数更小通常更快。负数处理算法只判断!0负数被视为非零无需改动。移动零到开头将条件nums[fast] ! 0改为nums[fast] 0并将非零值如 1补在末尾。允许复制新建数组先拷贝非零再补零最后复制回原数组时间 O(n)空间 O(n)。祝你在算法之路上越走越稳早日攻克每一道难题下次见 ✨
返回列表