Leetcode 126 两数之和 II - 输入有序数组 | 盛最多水的容器

1 题目

167. 两数之和 II - 输入有序数组

给你一个下标从 1 开始的整数数组 numbers ,该数组已按非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1]numbers[index2] ,则 1 <= index1 < index2 <= numbers.length

以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1index2

你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。

你所设计的解决方案必须只使用常量级的额外空间。

示例 1:

复制代码
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。

示例 2:

复制代码
输入:numbers = [2,3,4], target = 6
输出:[1,3]
解释:2 与 4 之和等于目标数 6 。因此 index1 = 1, index2 = 3 。返回 [1, 3] 。

示例 3:

复制代码
输入:numbers = [-1,0], target = -1
输出:[1,2]
解释:-1 与 0 之和等于目标数 -1 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。

提示:

  • 2 <= numbers.length <= 3 * 104
  • -1000 <= numbers[i] <= 1000
  • numbers非递减顺序 排列
  • -1000 <= target
  • <= 1000
  • 仅存在一个有效答案

2 代码实现

c++(自己写的没优化过)

cpp 复制代码
class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int left = 0 ;
        int right = numbers.size() - 1 ;
        vector<int> res ;
        while (left < right ){
            if (numbers[left ] + numbers[right] == target){
                res.push_back(left + 1 );
                res.push_back(right + 1 );
            }
            if (numbers[left ] + numbers[right] > target){
                right -- ;
            }else {
                left ++ ;
            }
        }
        return res ;
    }
};

js

javascript 复制代码
/**
 * @param {number[]} numbers
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(numbers, target) {
    let left = 0 ;
    let right = numbers.length - 1 ;

    while (left < right ){
        const curSum = numbers[left] + numbers[right] ;
        if (curSum === target){
            return [left + 1 , right + 1 ];
        }else if (curSum > target){
            right -- ;
        }else {
            left ++ ;
        }
    }
    return [];
};

思考

注意下标是从1开始,和数组默认的index是不同的。

自己写的代码!通过了,但是写得很丑

cpp 复制代码
class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int left = 0 ;
        int right = numbers.size() - 1 ;
        vector<int> res ;
        while (left < right ){
            if (numbers[left ] + numbers[right] == target){
                res.push_back(left + 1 );
                res.push_back(right + 1 );
            }
            if (numbers[left ] + numbers[right] > target){
                right -- ;
            }else {
                left ++ ;
            }
        }
        return res ;
    }
};

优化版题解(c++)

你想要优化这段在有序数组中寻找两数之和等于目标值的代码,核心是让代码更高效、更规范且可读性更好。当前代码的主要问题是找到答案后没有立即退出循环,会继续执行指针移动逻辑,存在不必要的计算。

优化思路

  1. 提前终止循环:找到符合条件的两个数后立即返回结果,避免无效的循环迭代。
  2. 代码精简:合并重复的和计算逻辑,减少冗余。
  3. 增强可读性:添加注释、规范变量命名,让代码意图更清晰。
  4. 边界处理:保持原有的双指针核心逻辑(这是有序数组两数之和的最优解法,时间复杂度 O (n),空间复杂度 O (1))。

优化后的完整代码

cpp 复制代码
#include <vector>
using namespace std;

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        // 双指针初始化:左指针指向数组头部,右指针指向数组尾部
        int left = 0;
        int right = numbers.size() - 1;
        
        while (left < right) {
            int current_sum = numbers[left] + numbers[right]; // 只计算一次和,减少冗余
            
            if (current_sum == target) {
                // 找到目标,直接返回(题目保证有且仅有一个解),无需继续循环
                return {left + 1, right + 1}; // 直接构造vector返回,更简洁
            } else if (current_sum > target) {
                // 和大于目标值,右指针左移(数组有序,减小和)
                right--;
            } else {
                // 和小于目标值,左指针右移(数组有序,增大和)
                left++;
            }
        }
        
        // 题目保证有解,此处仅为语法兜底
        return {};
    }
};

关键优化点解释

  1. 提前返回 :原代码找到结果后仍会执行后续的if/else逻辑移动指针,优化后找到current_sum == target时直接return,避免了无效的循环步骤,提升了执行效率(尤其是找到结果位置靠前的情况)。
  2. 减少重复计算 :将numbers[left] + numbers[right]的结果赋值给current_sum,原代码在两个if判断中都计算了一次,优化后仅计算一次,节省了计算资源。
  3. 简化返回逻辑 :原代码先创建空vectorpush_back,优化后直接用return {left + 1, right + 1}构造返回值,代码更简洁且减少了临时变量的创建。
  4. 注释增强:添加了关键逻辑的注释,新手能更快理解双指针的移动规则。

总结

  1. 核心优化是找到结果后立即返回,避免无效循环,这是提升执行效率的关键。
  2. 减少重复的和计算、简化返回逻辑,让代码更简洁且性能更好。
  3. 双指针法本身已是该问题的最优解法(时间 O (n)、空间 O (1)),优化后保持了最优复杂度,仅提升代码的执行效率和可读性。

3 题目

11. 盛最多水的容器

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

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

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

**说明:**你不能倾斜容器。

示例 1:

复制代码
输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

示例 2:

复制代码
输入:height = [1,1]
输出:1

提示:

  • n == height.length
  • 2 <= n <= 105
  • 0 <= height[i] <= 104

4 代码实现

c++

cpp 复制代码
class Solution {
public:
    int maxArea(vector<int>& height) {
        int res  = 0 ;
        int left = 0 ;
        int right = height.size() - 1 ;

        while (left < right ){
            int wid = right - left ;
            int curHeight = min(height[left] , height[right]);
            int cur = curHeight * wid ;
            res = max(cur ,res );

            if (height[left] < height[right]){
                left ++ ;
            }else{
                right -- ;
            }
        } 
        return res ;
    }
};

js

javascript 复制代码
/**
 * @param {number[]} height
 * @return {number}
 */
var maxArea = function(height) {
    let left = 0 ;
    let right = height.length - 1 ;
    let res = 0 ;

    while (left < right ){
        let wid = right - left ;
        let curHeight = height[left];
        if (height[left] > height[right]){
            curHeight = height[right] ;
        }
        let cur = wid * curHeight ;
        if (cur > res ){
            res = cur ;
        }
        if (height[left] < height[right]){
            left ++ ;
        }else{
            right -- ;
        }
    }  
    return res ;
};

思考

好像是在求面积,找两个点,第一个坐标的距离 * 第二个坐标的最小值 = 面积 , 把这个弄得最大就可以。

第一个想到的就是两层for循环暴力,感觉不是很妥当。我想了一下能不能用sort直接排序一下高度,但是一想又要有hashmap映射下标,我觉得还是O(n^2)的复杂度,还是不是很方便,双指针怎么写?

题解

你想解决 "盛最多水的容器" 这个问题,核心是找到用双指针法替代暴力枚举(O (n²))的高效解法(O (n)),并且需要一份详细的 C++ 实现代码。

解题思路(双指针核心逻辑)

  1. 面积计算公式 :容器的盛水量 = 两个指针的间距(宽度) × 两个指针所指高度的较小值(高度),即 area = (right - left) * min(height[left], height[right])
  2. 双指针初始位置 :左指针 left 从数组头部(0)开始,右指针 right 从数组尾部(n-1)开始,此时宽度最大。
  3. 指针移动规则
    • 盛水量由较矮的边决定,移动较矮的那个指针(因为移动较高的指针,宽度减小但高度不会增加,盛水量必然减少;而移动较矮的指针,有可能找到更高的边,从而增大盛水量)。
    • 如果两边高度相等,移动任意一个指针即可(不影响最终结果)。
  4. 遍历过程:每次计算当前指针组合的盛水量,更新最大值,直到左右指针相遇。

C++ 完整实现代码

cpp 复制代码
#include <vector>
#include <algorithm> // 用于min函数
using namespace std;

class Solution {
public:
    int maxArea(vector<int>& height) {
        int left = 0; // 左指针初始在数组头部
        int right = height.size() - 1; // 右指针初始在数组尾部
        int max_water = 0; // 记录最大盛水量,初始为0

        // 双指针遍历,直到左右指针相遇
        while (left < right) {
            // 计算当前容器的宽度:右指针下标 - 左指针下标
            int width = right - left;
            // 计算当前容器的有效高度:两个高度的较小值(水不会超过矮边)
            int current_height = min(height[left], height[right]);
            // 计算当前盛水量
            int current_water = width * current_height;

            // 更新最大盛水量:取当前值和历史最大值的较大者
            max_water = max(max_water, current_water);

            // 移动较矮的指针(核心逻辑)
            if (height[left] < height[right]) {
                left++; // 左指针右移
            } else {
                right--; // 右指针左移
            }
        }

        return max_water;
    }
};

// 测试示例
int main() {
    Solution s;
    // 示例1
    vector<int> height1 = {1,8,6,2,5,4,8,3,7};
    // 输出49
    printf("示例1结果:%d\n", s.maxArea(height1));
    
    // 示例2
    vector<int> height2 = {1,1};
    // 输出1
    printf("示例2结果:%d\n", s.maxArea(height2));
    
    return 0;
}

代码关键解释

  1. 变量初始化
    • leftright 分别指向数组两端,保证初始宽度最大;
    • max_water 初始为 0,用于存储遍历过程中找到的最大盛水量。
  2. 核心循环
    • 每次循环计算当前指针组合的宽度、有效高度、盛水量;
    • max 函数更新最大盛水量,避免手动写 if 判断;
    • 严格遵循 "移动较矮指针" 的规则,确保每一步都在向可能的更大盛水量靠近。
  3. 时间 / 空间复杂度
    • 时间复杂度:O (n),仅遍历数组一次;
    • 空间复杂度:O (1),仅使用了几个临时变量,无额外空间开销。

测试结果说明

  • 示例 1 输入 [1,8,6,2,5,4,8,3,7]:当 left=1(高度 8)、right=8(高度 7)时,宽度为 7,有效高度为 7,盛水量 = 7×7=49,是最大值;
  • 示例 2 输入 [1,1]:宽度为 1,有效高度为 1,盛水量 = 1×1=1,符合预期。

总结

  1. 双指针法的核心是移动较矮的指针,因为盛水量由矮边决定,移动高边无法提升有效高度,只会减少宽度;
  2. 该解法将时间复杂度从暴力法的 O (n²) 优化到 O (n),是本题的最优解;
  3. 代码逻辑简洁,关键是理解 "宽度最大优先,逐步优化高度" 的思路。

自己写的js又错了

javascript 复制代码
/**
 * @param {number[]} height
 * @return {number}
 */
var maxArea = function(height) {
    let left = 0 ;
    let right = height.length - 1 ;
    let res = 0 ;

    while (left < right ){
        let wid = right - left ;
        let curHeight = height[left];
        if (height[left] < height[right]){
            curHeight = height[right] ;
        }
        let cur = wid * curHeight ;
        if (cur > res ){
            res = cur ;
        }
        if (height[left] < height[right]){
            left ++ ;
        }else{
            right -- ;
        }
    }  
    return res ;
};

你这段 JS 代码的核心错误是计算有效高度时搞反了逻辑 ------ 盛水的有效高度应该取两个高度的较小值 ,但你代码里取成了较大值,这直接导致计算出的面积完全错误。

错误定位与分析

先看你代码中计算 curHeight 的关键片段:

javascript 复制代码
let curHeight = height[left];
if (height[left] < height[right]){
    curHeight = height[right] ; // 错误:这里取了较大值,而非较小值
}

举个例子:当 height[left]=8height[right]=7 时,你的代码会把 curHeight 设为 8,但实际容器的有效高度只能是 7(水会从矮的一边溢出),用 8 计算会得出远超实际的面积,结果自然错误。

5 小结

多练习,感觉越做越顺了,双指针这一块还可以了。加油,坚持!

相关推荐
科技林总2 小时前
【系统分析师】9.6 安全管理措施
学习
lxl13072 小时前
C++算法(5)位运算
java·c++·算法
tankeven2 小时前
HJ96 表示数字
c++·算法
嵌入式×边缘AI:打怪升级日志2 小时前
C语言算术赋值运算复习笔记
c语言·stm32·单片机·算法·51单片机·proteus·代码
lxl13072 小时前
C++算法(4)前缀和
开发语言·c++·算法
不想看见4042 小时前
Minimum Path Sum 基本动态规划:二维--力扣101算法题解笔记
算法·leetcode·动态规划
啊阿狸不会拉杆2 小时前
《计算机视觉:模型、学习和推理》第 7 章-复杂数据密度建模
人工智能·python·学习·算法·计算机视觉·t分布·复杂数据密度建模
啊哈哈121382 小时前
SQL学习笔记7:综合查询与高级技巧全解析 + LeetCode实战
笔记·sql·学习
Loo国昌2 小时前
【AI应用开发实战】00_StockPilotX技术博客专栏:从零构建生产级AI金融分析系统
人工智能·算法·语言模型·自然语言处理·金融·prompt