LeetCode100天Day4-盛最多水的容器与两数之和II

LeetCode100天Day4-盛最多水的容器与两数之和II:双指针算法的极致应用

摘要:本文详细解析了LeetCode中两道经典双指针算法题目------"盛最多水的容器"和"两数之和 II:输入有序数组"。通过对比暴力解法和双指针解法,深入分析算法的时间复杂度和空间复杂度,帮助读者掌握双指针算法的精髓。

目录

文章目录

  • LeetCode100天Day4-盛最多水的容器与两数之和II:双指针算法的极致应用
    • 目录
    • [1. 盛最多水的容器(Container With Most Water)](#1. 盛最多水的容器(Container With Most Water))
      • [1.1 题目描述](#1.1 题目描述)
      • [1.2 解题思路分析](#1.2 解题思路分析)
      • [1.3 代码实现与详细解析](#1.3 代码实现与详细解析)
      • [1.4 复杂度分析](#1.4 复杂度分析)
    • [2. 两数之和 II - 输入有序数组(Two Sum II - Input array is sorted)](#2. 两数之和 II - 输入有序数组(Two Sum II - Input array is sorted))
    • [3. 双指针算法思想总结](#3. 双指针算法思想总结)
      • [3.1 适用场景](#3.1 适用场景)
      • [3.2 常见双指针模式](#3.2 常见双指针模式)
      • [3.3 代码模板](#3.3 代码模板)
    • [4. 实战技巧与注意事项](#4. 实战技巧与注意事项)
      • [4.1 代码优化技巧](#4.1 代码优化技巧)
      • [4.2 常见陷阱](#4.2 常见陷阱)
      • [4.3 调试技巧](#4.3 调试技巧)
    • [5. 扩展思考](#5. 扩展思考)
      • [5.1 相关题目推荐](#5.1 相关题目推荐)
      • [5.2 进阶优化](#5.2 进阶优化)
    • [6. 总结与展望](#6. 总结与展望)
    • 参考资源
    • 文章标签

1. 盛最多水的容器(Container With Most Water)

1.1 题目描述

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

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

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

说明:你不能倾斜容器。

1.2 解题思路分析

暴力解法思路

首先想到的暴力解法是:对于每一对可能的线段组合,计算它们能容纳的水量,然后找出最大值。

暴力解法的时间复杂度为 O(n²),对于大型数据集会导致超时。

双指针解法核心思想

双指针解法的核心在于贪心策略

  1. 指针初始化 :一个指针在数组开头(left = 0),一个在数组末尾(right = len-1
  2. 移动策略 :每次移动较短的那根线段对应的指针
  3. 理论基础:移动较长的线段只会减少容器的宽度,而高度不会增加(因为较短的线段限制了高度)

关键洞察 :容器的容积由 min(height[left], height[right]) * (right - left) 决定。为了获得更大的容积,我们需要在保持可能的高度的同时,增加宽度或者找到更高的线段。

1.3 代码实现与详细解析

java 复制代码
class Solution {
    public int maxArea(int[] height) {
        int len = height.length;
        int tail_index = len - 1;        // 右指针
        int head = height[0];            // 左指针指向的线段高度
        int head_index = 0;              // 左指针
        int tail = height[tail_index];   // 右指针指向的线段高度

        // 计算初始容积
        int lower = head < tail ? head : tail;
        int curtemp = (tail_index - head_index) * lower;
        int max = curtemp;               // 最大容积

        // 双指针移动逻辑
        while(head_index < tail_index){
            if (head < tail) {
                // 左边线段更短,移动左指针
                head_index++;
                head = height[head_index];
            } else {
                // 右边线段更短或相等,移动右指针
                tail_index--;
                tail = height[tail_index];
            }

            // 计算当前容积
            lower = head < tail ? head : tail;
            curtemp = (tail_index - head_index) * lower;

            // 更新最大容积
            if (curtemp > max) {
                max = curtemp;
            }
        }
        return max;
    }
}

1.4 复杂度分析

指标 暴力解法 双指针解法
时间复杂度 O(n²) O(n)
空间复杂度 O(1) O(1)
实际执行时间 超时 2ms

2. 两数之和 II - 输入有序数组(Two Sum II - Input array is sorted)

2.1 题目描述

给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。

如果设这两个数分别是 numbers[index1]numbers[index2] ,则 1 <= index1 < index2 <= numbers.length

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

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

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

2.2 解题思路对比

解法一:暴力搜索(利用有序性优化)

这是最直观的解法,利用了数组有序的特性:

java 复制代码
class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int[] ans = new int[2];
        for(int i = 0; i < numbers.length-1; i++){
            int complement = target - numbers[i];
            // 内层循环从 i+1 开始
            for(int j = i+1; j < numbers.length; j++){
                // 利用有序性提前终止
                if(numbers[j] > complement) {
                    break;
                }
                // 找到答案
                if(numbers[i] + numbers[j] == target){
                    ans[0] = i+1;  // 下标从1开始
                    ans[1] = j+1;
                    return ans;
                }
            }
        }
        return ans;
    }
}

优化点分析

  • 利用数组有序的特性,当 numbers[j] > complement 时可以提前终止内层循环
  • 时间复杂度:最坏情况下仍为 O(n²),但平均情况有所改善
解法二:双指针(最优解)

双指针解法充分利用了数组有序的特性:

java 复制代码
class Solution {
    public int[] twoSum(int[] numbers, int target) {
        // 边界条件检查
        if(numbers == null || numbers.length < 2) return null;

        int head = 0, tail = numbers.length - 1;

        while(head < tail){
            int sum = numbers[head] + numbers[tail];

            if(sum > target) {
                // 和大于目标值,需要减小
                tail--;
            }
            else if(sum < target) {
                // 和小于目标值,需要增大
                head++;
            }
            else {
                // 找到答案
                return new int[]{head+1, tail+1};
            }
        }
        return null;
    }
}

2.3 双指针解法数学证明

设数组为 numbers,目标为 target,当前指针位置为 leftright

情况1numbers[left] + numbers[right] > target

  • 由于数组有序,对于任意 k > left,都有 numbers[k] ≥ numbers[left]
  • 因此 numbers[k] + numbers[right] ≥ numbers[left] + numbers[right] > target
  • 结论:必须减小右指针才能找到解

情况2numbers[left] + numbers[right] < target

  • 由于数组有序,对于任意 k < right,都有 numbers[k] ≤ numbers[right]
  • 因此 numbers[left] + numbers[k] ≤ numbers[left] + numbers[right] < target
  • 结论:必须增大左指针才能找到解

2.4 复杂度对比分析

对比项 暴力搜索优化版 双指针解法
时间复杂度 O(n²) O(n)
空间复杂度 O(1) O(1)
代码简洁度 中等 优秀
执行效率 较慢 极快
内存使用 常量级 常量级

3. 双指针算法思想总结

3.1 适用场景

双指针算法适用于以下场景:

  1. 有序数组:数组已经排序(升序或降序)
  2. 查找特定条件:需要找到满足特定条件的元素对或子数组
  3. 需要优化时间复杂度:从 O(n²) 优化到 O(n)

3.2 常见双指针模式

模式类型 特点 典型题目
对撞指针 左右指针向中间移动 两数之和、盛水容器
快慢指针 不同速度的指针 环形链表检测
滑动窗口 窗口大小可变 最小覆盖子串
分离指针 两个独立目标 三数之和

3.3 代码模板

java 复制代码
// 对撞指针模板
int left = 0, right = array.length - 1;
while (left < right) {
    int sum = array[left] + array[right];
    if (sum == target) {
        // 找到解
        return new int[]{left, right};
    } else if (sum < target) {
        left++;    // 需要更大的和
    } else {
        right--;   // 需要更小的和
    }
}

4. 实战技巧与注意事项

4.1 代码优化技巧

  1. 边界检查:始终检查输入数组的边界条件
  2. 提前终止:利用有序性提前终止无效搜索
  3. 避免重复计算:缓存中间结果减少重复计算

4.2 常见陷阱

注意事项

  • 下标从0还是从1开始:注意题目要求,本题要求从1开始
  • 整数溢出:虽然本题数据范围较小,但实际应用中要考虑
  • 重复使用元素:题目明确要求不能重复使用同一元素

4.3 调试技巧

java 复制代码
// 添加调试信息便于理解
System.out.println("left: " + left + ", right: " + right);
System.out.println("numbers[left]: " + numbers[left] + ", numbers[right]: " + numbers[right]);
System.out.println("current sum: " + (numbers[left] + numbers[right]));

5. 扩展思考

5.1 相关题目推荐

  1. 三数之和:LeetCode 第15题
  2. 四数之和:LeetCode 第18题
  3. 最接近的三数之和:LeetCode 第16题
  4. 三角形最小路径和:双指针思想应用

5.2 进阶优化

对于超大数据集,可以考虑:

  • 并行化处理:将数组分割后并行处理
  • 位运算优化:特定情况下的位运算技巧
  • 缓存优化:利用CPU缓存提高访问速度

6. 总结与展望

今天我们深入学习了双指针算法在两个经典题目中的应用:

  1. 盛最多水的容器:通过贪心策略和双指针,将时间复杂度从 O(n²) 优化到 O(n)
  2. 两数之和 II:充分利用数组有序特性,实现线性时间复杂度的优雅解法

核心收获

  • 双指针算法的本质是用空间换时间,但实际上并不需要额外空间
  • 有序性是双指针算法的重要前提
  • 贪心策略在双指针算法中扮演关键角色

下一步学习计划

  • 练习更多双指针相关题目
  • 学习滑动窗口等高级双指针技巧
  • 探索双指针在其他数据结构中的应用

互动时间:你还在哪些题目中见过双指针算法的应用?欢迎在评论区分享你的解题经验!


参考资源

文章标签

#LeetCode #算法 #双指针 #Java #数据结构

喜欢这篇文章吗?别忘了点赞、收藏和分享!你的支持是我创作的最大动力!

相关推荐
ZBritney2 小时前
JAVA中的多线程
java
whn19772 小时前
达梦数据库的整体负载变化查看
java·开发语言·数据库
小满、2 小时前
RabbitMQ:Fanout、Direct、Topic 交换机、队列声明与消息转换器
java·分布式·消息队列·rabbitmq·spring amqp
xie_pin_an2 小时前
深入解析 C 语言排序算法:从快排优化到外排序实现
c语言·算法·排序算法
Hcoco_me2 小时前
机器学习核心概念与主流算法(通俗详细版)
人工智能·算法·机器学习·数据挖掘·聚类
Hcoco_me2 小时前
嵌入式场景算法轻量化部署checklist
算法
咸鱼加辣2 小时前
【python面试】Python 的 lambda
javascript·python·算法
檀越剑指大厂2 小时前
【Idea系列】换行处理
java·ide·intellij-idea
Jerryhut2 小时前
sklearn函数总结十二 —— 聚类分析算法K-Means
算法·kmeans·sklearn