当前位置: 首页 > news >正文

从暴力到动态规划再到双指针:使用 Java 探索接雨水问题的不同解法

文章目录

    • 一、问题描述
    • 二、暴力法(Brute Force)
      • 思路
      • 实现代码
    • 三、动态规划法(Dynamic Programming)
      • 思路
      • 实现代码
    • 四、双指针法(Two Pointers)
      • 思路
      • 实现代码
    • 五、方法对比

在本文中,我们将探讨经典的接雨水问题,并提供多种解题思路和方法。通过逐步深入的方式,将了解到如何使用暴力法、动态规划法以及双指针法解决这一问题,并对它们进行性能对比。

一、问题描述

42.接雨水
在这里插入图片描述


二、暴力法(Brute Force)

思路

对于数组中的每一个元素,我们都可以找到它左边和右边的最大高度,然后根据这两个最大高度来确定当前元素能够接住的水量。

实现代码

public int trap(int[] height) {
    int totalWater = 0;
    for (int i = 1; i < height.length - 1; i++) {
        int maxLeft = 0, maxRight = 0;
        // 寻找左边最高的柱子
        for (int j = i; j >= 0; j--) {
            maxLeft = Math.max(maxLeft, height[j]);
        }
        // 寻找右边最高的柱子
        for (int j = i; j < height.length; j++) {
            maxRight = Math.max(maxRight, height[j]);
        }
        // 计算当前位置可以存储的水
        totalWater += Math.min(maxLeft, maxRight) - height[i];
    }
    return totalWater;
}

时间复杂度:O(n^2),因为对于每个元素都需要遍历其左右两边来寻找最大值。

空间复杂度:O(1)。


三、动态规划法(Dynamic Programming)

思路

为了避免重复计算每个位置的左右最大高度,我们可以预先计算并存储这些信息。这样,当我们计算某个位置能接多少水时,可以直接查表得到左右最大高度。

实现代码

public int trap(int[] height) {
    if (height == null || height.length == 0) return 0;
    
    int n = height.length;
    int[] leftMax = new int[n], rightMax = new int[n];
    
    // 初始化最左边和最右边的边界条件
    leftMax[0] = height[0];
    rightMax[n - 1] = height[n - 1];
    
    // 填充leftMax数组
    for (int i = 1; i < n; i++) {
        leftMax[i] = Math.max(leftMax[i - 1], height[i]);
    }
    
    // 填充rightMax数组
    for (int i = n - 2; i >= 0; i--) {
        rightMax[i] = Math.max(rightMax[i + 1], height[i]);
    }
    
    // 计算总储水量
    int totalWater = 0;
    for (int i = 0; i < n; i++) {
        totalWater += Math.min(leftMax[i], rightMax[i]) - height[i];
    }
    
    return totalWater;
}

时间复杂度:O(n),因为我们只需要遍历数组三次。

空间复杂度:O(n),需要额外的空间来存储左右最大高度。


四、双指针法(Two Pointers)

思路

利用两个指针分别从数组的两端向中间移动,并同时维护左右两侧的最大高度。这种方法可以在一次遍历中解决问题,无需额外的空间。

实现代码

public int trap(int[] height) {
    if (height == null || height.length == 0) return 0;
    
    int left = 0, right = height.length - 1;
    int maxLeft = 0, maxRight = 0;
    int totalWater = 0;
    
    while (left < right) {
        if (height[left] < height[right]) {
            if (height[left] >= maxLeft) {
                maxLeft = height[left];
            } else {
                totalWater += maxLeft - height[left];
            }
            left++;
        } else {
            if (height[right] >= maxRight) {
                maxRight = height[right];
            } else {
                totalWater += maxRight - height[right];
            }
            right--;
        }
    }
    
    return totalWater;
}

时间复杂度:O(n),只需遍历一次数组。

空间复杂度:O(1),只使用了常数级别的额外空间。


五、方法对比

方法时间复杂度空间复杂度优点缺点
暴力法O(n^2)O(1)易于理解和实现效率低下,不适用于大数据集
动态规划法O(n)O(n)高效处理中等规模数据需要额外的存储空间
双指针法O(n)O(1)最优的时间和空间复杂度理解难度较高

通过上述三种方法的介绍和比较,我们可以看到双指针法以其优秀的时空复杂度成为解决该问题的最佳选择。然而,理解不同的方法有助于我们在面对类似问题时能够灵活运用算法思维,选择最适合当前场景的解决方案。

相关文章:

  • 处理Long类型长度超长导致前端精度丢失问题
  • Python用户管理系统深度解析(附源码):从类设计到安全实现的完整指南
  • 「数据可视化 D3系列」入门第一章:Hello D3.js
  • 数据库实战篇,JSON对象在Kooboo中的实际应用(二)
  • SQL注入之时间盲注攻击流程详解
  • SLAM文献之DM-VIO: Delayed Marginalization Visual-Inertial Odometry
  • 大模型之Hugging Face
  • 信奥还能考吗?未来三年科技特长生政策变化
  • 【开发教程】学生团队项目开发协调管理文档库构建以及使用指南
  • #4 为什么要物联以及 物联网的整体结构
  • linux tracepoint系列宏定义(TRACE_EVENT,DEFINE_TRACE等)展开过程分析之三 define_trace.h头文件
  • 【blender小技巧】Blender导出带贴图的FBX模型,并在unity中提取材质模型使用
  • telepresence使用指南
  • 【LH-开发记录】
  • Dockerfile 学习指南和简单实战
  • 一文介绍关于多模态的基础知识 !!
  • PHP弱类型hash比较缺陷
  • 高速连接器设计的技术挑战和解决方案
  • Dify智能体平台源码二次开发笔记(5) - 多租户的SAAS版实现(2)
  • 添加登录和注册功能
  • 生态环境法典草案拟初审:应对气候变化等问题将作原则性规定
  • 中国专家组赴缅开展地震灾害评估工作
  • 鼓励每位学生为优秀定义,上海奉贤这所学校有何特色?
  • 上海车展上的双向奔赴:跨国车企融入中国创新,联手“在中国,为全球”
  • 又一名被拐孩子找到!29年后,在警方帮助下云南男子寻子成功
  • 新任遂宁市委副书记王忠诚已任市政府党组书记