双指针算法经典案例:LeetCode 283. 移动零(Java详解)

一、前言

在算法面试和编程学习中,++双指针++ 是一种非常高效且常用的技巧。它通过两个下标(或指针)在数组(或链表)上进行移动,从而在 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 三个区间的划分

在任意时刻,数组可以被划分为三个区间:

  1. [0, dest]:已处理的非零元素区。

  2. [dest + 1, cur - 1]:已处理的零元素区(这部分其实可以忽略,因为我们不关心零的顺序)。

  3. [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. 移动零​ 这道题,我深入学习了:

  • 双指针算法​ 的基本思想和应用场景。

  • 数组划分 ​ 和 区间管理​ 的技巧。

  • 原地操作​ 的优化策略。

  • 快速排序​ 的关联,提升算法思维广度。

双指针是算法面试中的常客,掌握它不仅有助于解题,更能培养我们对数据结构的深刻理解和灵活运用能力。


九、参考资料


下期预告:我将继续讲解双指针的其他经典题型,如"两数之和 II"、"盛最多水的容器"等,敬请期待!


📌 **关注我,一起进阶算法之路!**​

算法 #双指针 #LeetCode #Java #移动零 #编程学习 #优选算法专栏

相关推荐
上弦月-编程1 小时前
指针编程:高效内存管理核心
java·数据结构·算法
xieliyu.1 小时前
Java手搓数据结构:栈与队列模拟实现
java·数据结构·学习
清水白石0081 小时前
深入 Python 循环引用与垃圾回收:如何应对内存管理的挑战
java·jvm·python
_Evan_Yao1 小时前
从 IP 路由到 Agent 路由:最长前缀匹配如何帮你分发任务?
java·网络·后端·网络协议·tcp/ip
人道领域1 小时前
【数据结构与算法分析】二叉树面试通关手册:遍历图解 · 分类对比 · 代码模板
数据结构·算法·leetcode·深度优先
水蓝烟雨1 小时前
2901. 最长相邻不相等子序列 II
算法·leetcode
郝学胜-神的一滴2 小时前
二分类任务核心:BCE 损失函数从原理到 PyTorch 实战
人工智能·pytorch·python·算法·机器学习·分类·数据挖掘
.5482 小时前
Two Pointers(双指针)
java·数据结构·算法
sali-tec2 小时前
C# 基于OpenCv的视觉工作流-章58-相机标定
图像处理·人工智能·数码相机·opencv·算法·计算机视觉