Leetcode-day4:从「移动零」到「盛最多水的容器」

双指针入门实战:从「移动零」到「盛最多水的容器」------我在 Python 中理解"指针"的旅程

关键词 :双指针|移动零(LeetCode #283)|盛最多水的容器(LeetCode #11)|Python 指针思想|原地操作
感悟 :Python 的"指针"不像 C 那样需要 *&,而是通过变量直接引用下标或对象,简洁而强大。


一、引子:我对"指针"的重新认识

刚开始接触算法题时,总以为"指针"是 C/C++ 的专属概念。直到刷了「移动零」和「盛最多水的容器」这两道题,我才恍然大悟:

在 Python 中,指针 = 用变量表示数组下标或对象引用

双指针 = 两个变量分别指向数组的不同位置,协同工作

不需要 int* p,不需要取地址 &,只需要:

python 复制代码
left = 0
right = len(arr) - 1

这两个变量,就是我的"指针"。


二、第一题:移动零(LeetCode #283)

🔍 题目要求

将数组中所有 0 移动到末尾,保持非零元素的相对顺序原地修改

💡 我的解法思路

我采用两阶段双指针策略:

  1. 第一遍 :用 fast 遍历数组,slow 记录"下一个非零应放的位置"
    • 遇到非零 → nums[slow] = nums[fast],然后 slow += 1
  2. 第二遍 :从 slow 到末尾,全部设为 0
✅ 我的代码
python 复制代码
class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        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] = 0

🌟 我的收获

这不是传统指针,而是用整数变量模拟指针行为 ------slowfast 就是两个"下标指针"。


🔍 官方解法:一次遍历 + 交换

官方代码更精炼,只用一次遍历:

python 复制代码
class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        left = right = 0
        while right < len(nums):
            if nums[right] != 0:
                nums[left], nums[right] = nums[right], nums[left]  # ← 关键!
                left += 1
            right += 1
❓ 我的疑问:为什么交换可行?

起初我不理解这行交换的逻辑。后来画图才明白:

  • 等价于:

    python 复制代码
    # 临时保存右边的值(Python 自动完成)
    temp1 = nums[right]
    temp2 = nums[left]
    nums[left] = temp1
    nums[right] = temp2

    效果就是:交换 nums[left]nums[right] 的值

✅ 本质和我的方法一样,只是用交换避免了第二次循环补 0,更高效!

关键洞察:为什么 nums[left] 一定是 0?

这是理解整个算法的核心!

🤔 观察 left 的含义:

  • left 始终指向下一个应该放非零元素的位置
  • 而且,在 left 左侧([0, left)已经全是非零元素了

那么,left 当前位置是什么?

  • 因为数组中只有 0 和非零,
  • 而非零都已经被"处理"到左边了,
  • 所以 nums[left] 要么是 0,要么还没被访问(但初始是 0 或会被覆盖)

👉 在我们的例子中:

  • 第一次交换前:left=0, nums[0]=0
  • 第二次交换前:left=1, nums[1]=0(因为上一步把 1 换到了位置 0,0 被换到位置 1)
  • 第三次交换前:left=2, nums[2]=0

✅ 所以,每次交换都是:把一个非零(nums[right])和一个 0(nums[left])互换位置

这就实现了:

  • 非零元素逐步移到前面
  • 0 被"挤"到后面

三、第二题:盛最多水的容器(LeetCode #11)

🔍 题目要求

给定 n 个非负整数 height[i] 表示竖线高度,找出两条线,使得它们与 x 轴构成的容器装水最多

面积公式:area = (right - left) * min(height[left], height[right])


❌ 我的暴力解法(超时)

python 复制代码
class Solution:
    def maxArea(self, height: List[int]) -> int:
        max_area = 0
        n = len(height)
        for short in range(n-1):
            long = short + 1
            while long != n:
                current_area = (long - short) * min(height[short], height[long])
                max_area = max(max_area, current_area)
                long += 1
        return max_area
  • 时间复杂度:O(n²)
  • 对于 n=10⁵ 的数据,必然超时 ❌

✅ 我的双指针优化解法

我意识到:面积由"宽度"和"短板"决定 。于是采用左右指针向中间收缩的策略:

  • 初始化:slow = 0(左),fast = len(height)-1(右)
  • 每次计算当前面积
  • 移动较短的那一边(因为移动长边不会增加面积,只有换掉短板才可能更大)
✅ 我的代码
python 复制代码
class Solution:
    def maxArea(self, height: List[int]) -> int:
        slow = 0
        fast = len(height) - 1
        max_area = 0
        while slow < fast:
            if height[slow] <= height[fast]:
                current_area = (fast - slow) * height[slow]
                slow += 1
            else:
                current_area = (fast - slow) * height[fast]
                fast -= 1
            max_area = max(max_area, current_area)
        return max_area

🌟 关键洞察
移动短板,才有机会找到更大的面积。这是贪心 + 双指针的经典结合!

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

深入理解:Python 的"引用"机制

🔑 关键概念:

  • 一切皆对象
  • 变量存储的是对象的引用(内存地址的抽象)
  • 赋值 = 让变量指向同一个对象

🧪 示例 1:可变对象(如 list、自定义类)

python 复制代码
a = [1, 2, 3]
b = a           # b 和 a 指向同一个 list 对象
b.append(4)
print(a)        # [1, 2, 3, 4] → a 也被改变了!

✅ 这就像两个指针指向同一块内存。

🧪 示例 2:不可变对象(如 int、str、tuple)

python 复制代码
x = 10
y = x
y = 20
print(x)  # 10 → x 不变

❌ 因为 int 不可变,y = 20 是让 y 指向新对象,不影响 x

💡 所以:只有可变对象才能实现"通过一个引用修改,另一个看到变化"的指针效果


四、我的核心收获总结

维度 收获
Python 指针思想 不需要显式指针,用变量表示下标就是指针
双指针模式 两个变量协同工作: • 移动零:slow(写入位)、fast(读取位) • 盛水容器:leftright(两端向中间)
原地操作技巧 通过覆盖或交换,避免新建数组
算法优化意识 从 O(n²) 暴力 → O(n) 双指针,理解"为什么能优化"比记住代码更重要
代码风格 变量命名清晰(slow/fastleft/right)让逻辑一目了然

五、两类双指针模式对比

题型 指针移动方式 典型场景
同向双指针 slowfast 从左往右 移动零、删除重复项、滑动窗口
相向双指针 left 从左,right 从右,向中间靠拢 盛最多水、两数之和 II、回文判断

💡 口诀
"同向压缩,相向试探"

相关推荐
阿虎儿2 小时前
文档对比算法的历史演进
算法
拿我格子衫来2 小时前
搭建公司产品wiki的开源框架选型,注重介绍wikijs框架
程序人生·信息可视化·职场和发展
CoderCodingNo3 小时前
【CSP】CSP-XL 2025辽宁复赛真题-第四题, 购物(buy)
算法
nuowenyadelunwen3 小时前
Harvard CS50 Problems Set 5
数据结构·harvard cs50·树,哈希表,链表
mjhcsp3 小时前
P14795 [JOI 2026 二次预选] 分班 / Class Division
数据结构·c++·算法
闻缺陷则喜何志丹3 小时前
【计算几何 最短路 动态规划】P1354 房间最短路问题
数学·算法·动态规划·最短路·计算几何·洛谷
圣保罗的大教堂3 小时前
leetcode 840. 矩阵中的幻方 中等
leetcode
girl-07263 小时前
2025.12.29实验题目分析总结
数据结构·算法
点云SLAM3 小时前
Truncated Least Squares(TLS 截断最小二乘)算法原理
算法·slam·位姿估计·数值优化·点云配准·非凸全局优化·截断最小二乘法