[C++算法]盛最多水的容器(双指针算法)

前言

「盛最多水的容器」是力扣上一道经典的双指针题目。给定一个整数数组,数组中的每个值代表一根垂直于 x 轴的柱子高度,我们需要从中选出两根柱子,使它们与 x 轴围成的容器能够容纳最多的水。这个问题看似需要枚举所有柱子组合,但借助双指针的单调性,可以在一次遍历内高效求解。

双指针解法的核心思想是:从数组两端各放一个指针,每次比较两根柱子的高度,移动较矮一侧的指针向内收缩。因为容器的高度由较短的柱子决定,移动较矮一侧才有可能让高度变大,从而获得更大的容积。通过不断更新最大体积,最终得到答案。

题目描述:

题目来源:力扣「盛最多水的容器」

给定一个长度为 n 的整数数组 height。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i])。

找出其中的两条线,使它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明: 你不能倾斜容器。

横坐标表示数组的下标 i,纵坐标表示数组的值即 nums[i]。

算法原理:

利用单调性,采用双指针的算法解决此问题。

值得注意的是"盛水的体积大小取决于最短的那块木板"。

笔记:盛最多水的容器(双指针解法)

题目核心

容器盛水量公式:V = h × w

  • w:左右两个柱子之间的宽度(下标之差)
  • h:容器高度,由左右两根柱子中较短的那一根决定

示例数组:[1, 8, 6, 2, 5, 4, 8, 3, 7]

双指针思路

  1. 初始化:左指针 left 放在数组最左端,右指针 right 放在数组最右端。
  2. 循环终止条件:left 与 right 相遇时停止。
  3. 指针移动规则:比较左右两个柱子高度,移动高度更小的那一侧指针向内收缩。

分步图解示例

以示例数组 [1, 8, 6, 2, 5, 4, 8, 3, 7] 为例,逐步演示双指针的移动过程。下表记录了每一步中左右指针的位置、对应柱子高度、容器宽度、当前体积以及历史最大体积。

步骤 left right heightleft heightright 宽度 w 体积 V 最大体积 Vmax 移动指针
1 0 8 1 7 8 8 8 left++
2 1 8 8 7 7 49 49 right--
3 1 7 8 3 6 18 49 right--
4 1 6 8 8 5 40 49 left++
5 2 6 6 8 4 24 49 left++
6 3 6 2 8 3 6 49 left++
7 4 6 5 8 2 10 49 left++
8 5 6 4 8 1 4 49 left++

从表格中可以看到,当 left 指向下标 1(高度 8)、right 指向下标 8(高度 7)时,容器体积达到最大值 49,这也是本题的最终答案。整个过程中,左右指针从两端向中间靠拢,每个元素最多被访问一次,因此时间复杂度为 O(n)。

移动原理(单调性)

向内收缩时,宽度 w 一定会变小,分两种情况:

  1. 若移动较短柱子,新柱子高度 > 当前短柱高度:高度 h 不变,但宽度 w 减小,总体积 V 变小;
  2. 若移动较短柱子,新柱子高度 < 当前短柱高度:高度 h 减小,宽度 w 也减小,总体积 V 变小。

长柱子一侧不用移动:因为当前短柱已经限制了容器高度,向内移动长柱子只会让宽度变小,不可能得到更大的容积。所以直接舍弃当前较短的柱子,向内移动指针。

  1. 每次移动指针后,重新计算当前容器体积,持续更新记录最大体积 Vmax。
cpp 复制代码
class Solution 
{
public:
    int maxArea(vector<int>& height) 
    {
        int left = 0;
        int right = height.size()-1;
        //记录体积的最大值
        int ret = 0;
        while(left < right)
        {
            //宽度为左减去右
           int v = min(height[left],height[right])*(right -left);
           //计算完体积 更新一下最大的体积
           ret =max(v,ret);
           //移动指针
            if(height[left]<height[right])
            {
                left++;
            }
            else
            {
                right--;
            }
        }
        return ret;

    }
};

复杂度分析

双指针解法的时间复杂度为 O(n),空间复杂度为 O(1)。

时间复杂度为 O(n) 的原因:左右指针分别从数组两端出发,每次循环只移动其中一个指针,且两个指针相遇时循环结束。在整个过程中,每个元素最多只会被访问一次,因此总的操作次数与数组长度 n 成正比。

空间复杂度为 O(1) 的原因:算法只使用了 left、right、ret 等常数个额外变量,没有借助额外的数据结构,因此空间占用与输入规模无关。

总结

双指针解法是解决「盛最多水的容器」问题的高效方法,其关键在于利用容器的短板效应:每次移动较矮一侧的指针,在宽度减小的同时有机会提升容器高度,从而在 O(n) 时间内找到最大容积。

该解法适用于需要在一维数组中寻找满足某种约束的最优区间问题,尤其是当问题具有单调性、且最优解可以通过收缩边界逐步逼近时。理解这一思路,有助于举一反三地解决其他双指针类题目。

相关推荐
Sarvartha4 小时前
final 关键字
java·开发语言
fpcc4 小时前
c++编程实践—堆和栈越界调试
c++
yi0114 小时前
LeetCode 219:存在重复元素 II——哈希表记录“最近一次出现的位置”
数据结构·人工智能·笔记·python·算法·leetcode·哈希表
程序员Sunday4 小时前
JavaScript 事件循环面试题,宏任务与微任务怎么执行|Sunday面试指南
开发语言·javascript·面试·校招·事件循环·程序员sunday
zhangzeyuaaa5 小时前
Ruby 多线程、GVL(GIL)与 Mutex 完全指南
开发语言·前端·ruby
weiabc6 小时前
MSYS2 UCRT64 + g++ C++ 中文输出乱码 完整全过程
c++
谢亮_vipxieliang6 小时前
用 PHP 构建轻量级 REST API:从路由到鉴权的完整实践
开发语言·后端·php
思无邪667 小时前
用 AI 做 JS 逆向:从抓包到复现的完整方法论
开发语言·javascript·人工智能
灯澜忆梦7 小时前
【面向对象编程C++】| 基础语法
java·c++·算法
小蒋观天下7 小时前
专项方案:大场景港口AI安防、多干扰环境下的算法调优与落地实操
人工智能·深度学习·算法·安全·机器学习·计算机视觉·ai大模型