从《lc42 接雨水》到《lc84 柱状图中的最大矩形》

1 LC42 接雨水

在这里插入图片描述

1.1 答案

解法四:双指针
动态规划中,我们常常可以对空间复杂度进行进一步的优化。

例如这道题中,可以看到,max_left [ i ] 和 max_right [ i ] 数组中的元素我们其实只用一次,然后就再也不会用到了。所以我们可以不用数组,只用一个元素就行了。我们先改造下 max_left。

public int trap(int[] height) {
    int sum = 0;
    int max_left = 0;
    int[] max_right = new int[height.length];
    for (int i = height.length - 2; i >= 0; i--) {
        max_right[i] = Math.max(max_right[i + 1], height[i + 1]);
    }
    for (int i = 1; i < height.length - 1; i++) {
        max_left = Math.max(max_left, height[i - 1]);
        int min = Math.min(max_left, max_right[i]);
        if (min > height[i]) {
            sum = sum + (min - height[i]);
        }
    }
    return sum;
}

作者:windliang
链接:https://leetcode.cn/problems/trapping-rain-water/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

我们成功将 max_left 数组去掉了。但是会发现我们不能同时把 max_right 的数组去掉,因为最后的 for 循环是从左到右遍历的,而 max_right 的更新是从右向左的。

所以这里要用到两个指针,left 和 right,从两个方向去遍历。

那么什么时候从左到右,什么时候从右到左呢?根据下边的代码的更新规则,我们可以知道

max_left = Math.max(max_left, height[i - 1]);

height [ left - 1] 是可能成为 max_left 的变量, 同理,height [ right + 1 ] 是可能成为 right_max 的变量。

只要保证 height [ left - 1 ] < height [ right + 1 ] ,那么 max_left 就一定小于 max_right。

因为 max_left 是由 height [ left - 1] 更新过来的,而 height [ left - 1 ] 是小于 height [ right + 1] 的,而 height [ right + 1 ] 会更新 max_right,所以间接的得出 max_left 一定小于 max_right。

反之,我们就从右到左更。

  public int trap(int[] height) {

        int n=height.length;
        int ans=0;
        int lm=-1,rm=-1;
        int l=1,r=n-2;
        while(l<=r){
            if(height[l-1]<height[r+1]){ // #A
                lm=Math.max(lm,height[l-1]);
                if(lm>height[l]){
                    ans+=lm-height[l];
                }
                l++;
            }else{
                rm=Math.max(rm,height[r+1]);
                if(rm>height[r]){
                    ans+=rm-height[r];
                }
                r--;
            }
        }
        return ans;
    }

1.2 解析 #A 处的代码if(height[l-1]<height[r+1]), max_left不是还没有基于height[left - 1]更新吗,为什么能做出(由于max_left是基于高度[left - 1]更新的,并且高度[left - 1]小于高度[right + 1],这更新了max_right,因此逻辑上可以得出max_left将小于或等于max_right。)的推断?这里是不是默认初始以及之后的max_left <=max_right,所以只需要比较height[left - 1]和heightright- 1

您的理解是正确的。在这个算法中,确实有一个隐含的假设,即在执行到当前步骤之前,max_left 通常小于或等于 max_right。这个假设是基于算法如何运行和如何更新这两个值的。

当算法比较 height[left - 1]height[right + 1] 时,它实际上是在确定下一步应该如何移动指针,以便有效地计算积水量。这里的关键逻辑是:

  1. 如果 height[left - 1] < height[right + 1]:

    • 这意味着左侧的边界较低。因此,我们可以假设 max_left(已经是左边的最高点)小于或等于 max_right
    • 在这种情况下,移动左指针是安全的,因为即使左边的墙较低,右边也有足够高的墙(max_right)来保持水。
  2. 如果 height[left - 1] >= height[right + 1]:

    • 这表明右侧的边界较低,或者至少与左边相等。
    • 因此,移动右指针是更安全的选择,因为左边有足够高的墙(max_left)来保持水。
  3. 更新 max_leftmax_right:

    • 在每一步中,max_leftmax_right 都会根据当前指针位置的情况进行更新。

总的来说,这个算法的核心在于它尝试从较低的一侧开始计算积水量,这样可以保证在那一侧的计算过程中,另一侧总是有足够高的边界来支撑水位。这种方法使得算法既高效又准确。

2 LC84. 柱状图中最大的矩形

在这里插入图片描述

2.1 解题

推荐题解:lc84 柱状图中的最大矩形

class Solution {

	// 时间复杂度进一步常数级优化
    public int largestRectangleArea(int[] heights) {
        int n=heights.length;

        int ans=0;
        
        int[]l=new int[n];
        int[]r=new int[n];
        Arrays.fill(r,n);
        Deque<Integer>st=new LinkedList<>();
        int i=0;
        while(i<n){
            while(!st.isEmpty()&&heights[st.peekLast()]>=heights[i]){
                r[st.pollLast()]=i;
            }
            l[i]=st.isEmpty()?-1:st.peekLast();

            st.addLast(i);
            i++;
        }
    

        for(i=0;i<n;i++){
           ans=Math.max(ans, (r[i]-l[i]-1)*heights[i]);
        }
        return ans;
    }
    // 时间复杂度优化
    public int largestRectangleArea4(int[] heights) {
        int n=heights.length;

        int ans=0;
        
        int[]l=new int[n];
        int[]r=new int[n];
 
        Deque<Integer>st=new LinkedList<>();
        int i=0;
        while(i<n){
            while(!st.isEmpty()&&heights[st.peekLast()]>=heights[i]){
                st.pollLast();
            }
            l[i]=st.isEmpty()?-1:st.peekLast();

            st.addLast(i);
            i++;
        }
        st.clear();
        i=n-1;
        while(i>=0){
            while(!st.isEmpty()&&heights[st.peekLast()]>=heights[i]){
                st.pollLast();
            }
            r[i]=st.isEmpty()?n:st.peekLast();


            st.addLast(i);
            i--;
        }

        for(i=0;i<n;i++){
           ans=Math.max(ans, (r[i]-l[i]-1)*heights[i]);
        }
        return ans;
    }
    public int largestRectangleArea3(int[] heights) {
        int n=heights.length;

        int ans=0;
        // 按高度遍历,这里的遍历的是数组中所有出现的高度
        for(int i=0;i<n;i++){
            
            int j=i;
            while(j>=0&&heights[j]>=heights[i]){
                j--;
            }

            int k=i;
            while(k<n&&heights[k]>=heights[i]){
                k++;
            }
            ans=Math.max(ans,(k-j-1)*heights[i]);
        }
        
        return ans;
    }
    public int largestRectangleArea2(int[] heights) {
        int n=heights.length;

        int ans=0;
        for(int i=0;i<n;i++){
            int min=Integer.MAX_VALUE;
            for(int j=i;j<n;j++){
                min=Math.min(min, heights[j]);
                ans=Math.max(ans,(j-i+1)*min);
            }
        }
        return ans;
    }
}

3 相似之处

   // lc84 柱状图中的最大矩形 : 时间复杂度优化
    public int largestRectangleArea4(int[] heights) {
        int n=heights.length;

        int ans=0;
        
        int[]l=new int[n];
        int[]r=new int[n];
 
        Deque<Integer>st=new LinkedList<>();
        int i=0;
        while(i<n){
            while(!st.isEmpty()&&heights[st.peekLast()]>=heights[i]){
                st.pollLast();
            }
            l[i]=st.isEmpty()?-1:st.peekLast();

            st.addLast(i);
            i++;
        }
        st.clear();
        i=n-1;
        while(i>=0){
            while(!st.isEmpty()&&heights[st.peekLast()]>=heights[i]){
                st.pollLast();
            }
            r[i]=st.isEmpty()?n:st.peekLast();


            st.addLast(i);
            i--;
        }

        for(i=0;i<n;i++){
           ans=Math.max(ans, (r[i]-l[i]-1)*heights[i]);
        }
        return ans;
    }
	// lc42 接雨水
  public int trap(int[] height) {

        int n=height.length;
        int ans=0;
        int lm=-1,rm=-1;
        int l=1,r=n-2;
        while(l<=r){
            if(height[l-1]<height[r+1]){
                lm=Math.max(lm,height[l-1]);
                if(lm>height[l]){
                    ans+=lm-height[l];
                }
                l++;
            }else{
                rm=Math.max(rm,height[r+1]);
                if(rm>height[r]){
                    ans+=rm-height[r];
                }
                r--;
            }
        }
        return ans;
    }


3.1 这两个题解很相似

确实,从您提供的 LeetCode 问题 42 (“接雨水”) 和 问题 84 (“柱状图中最大的矩形”) 的解题代码中,我们可以看到一些相似之处。尽管两个问题的实际目标不同,它们的解法都涉及到一些共同的编程技巧和思路。

相似点

  1. 单调栈的应用:

    • 在问题 84 的解法中,使用单调栈(Deque<Integer> st)来维护一个单调递增的高度索引,这有助于确定每个条形的左右边界。
    • 这种栈的应用在处理数组中的连续区间问题时非常有效,如寻找左右两侧第一个小于当前元素的位置。
  2. 双指针技术:

    • 在问题 42 的解法中,使用左右双指针(lr)来从两端向中间遍历数组。
    • 这种技术允许在一次遍历中比较和处理两端的元素,是处理此类问题的常见方法。
  3. 局部最大/最小值的更新:

    • 在问题 42 中,使用 lmrm(左侧和右侧的最大高度)来更新局部最大值,并用这些值来计算积水量。
    • 在问题 84 中,使用栈来计算每个条形的局部左右边界,再计算最大矩形面积。
  4. 动态计算过程中的条件判断:

    • 在问题 42 中,通过比较 height[l-1]height[r+1] 来决定是移动左指针还是右指针。
    • 在问题 84 中,通过比较 heights[st.peekLast()]heights[i] 来决定是继续在栈中添加新元素还是进行弹出操作。

不同点

虽然它们在某些解题技巧上相似,但这两个问题的实际应用是不同的:

  • 问题 42 (“接雨水”) 要求计算在不规则形状的容器中积累的雨水总量。
  • 问题 84 (“柱状图中最大的矩形”) 要求找出由不同高度的条形构成的最大矩形面积。

总体来说,虽然这两个问题的解法在某些编程技术上有交叉,但它们分别解决了不同的问题。这种技术上的相似性突显了数据结构和算法在解决不同问题时的通用性和灵活性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/147619.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Niushop单商户及多商户v5商城系统第三方商业插件cps联盟视频购物及多包装库存转换的安装

一、后端安装 把video文件夹直接上传到addon目录下即可登录后台&#xff0c;设置->系统维护->插件管理->未安装插件&#xff0c;找到插件直接安装即可 3.在营销->营销中心->营销活动&#xff0c;找到视频列表这个插件&#xff0c;点击进去配置视频即可 4.装…

13.(vue3.x+vite)组件间通信方式之provide与inject

前端技术社区总目录(订阅之前请先查看该博客) 示例效果 依赖注入Provide / Inject 在父子组件传递数据时,通常使用的是 props 和 emit,父传子时,使用的是 props,如果是父组件传孙组件时,就需要先传给子组件,子组件再传给孙组件,如果多个子组件或多个孙组件使用时,就…

社区论坛小程序源码系统,功能齐全,页面简洁,前端+后端+完整部署教程

现如今&#xff0c;社区论坛已经成为人们交流思想&#xff0c;分享经验&#xff0c;获取信息的重要平台。近年来&#xff0c;小程序的出现更是改变了传统的网站建设方式&#xff0c;让用户体验更加便捷&#xff0c;高效。今天源码小编来和大家分享一款社区论坛小程序源码系统&a…

最强大模型训练芯片H200发布!141G大内存,AI推理最高提升90%,还兼容H100

梦晨 克雷西 发自 凹非寺 量子位 | 公众号 QbitAI 英伟达老黄&#xff0c;带着新一代GPU芯片H200再次炸场。 官网毫不客气就直说了&#xff0c;“世界最强GPU&#xff0c;专为AI和超算打造”。 听说所有AI公司都抱怨内存不够&#xff1f; 这回直接141GB大内存&#xff0c;与…

IDEA创建JavaFX项目

1、New -> Project 2、选择JavaFX 配置项目名&#xff0c;包名&#xff0c;lib包管理工具&#xff0c;JDK版本&#xff08;注&#xff0c;JDK版本最低需要11&#xff09; 3、选择lib包 根据自己需求选择 lib包介绍 BootstrapFX&#xff1a;BootstrapFX 是一个为 JavaFX 提…

mysql数据库超过最大连接数

mysql 超过数据库最大连接数解决办法 1、报错信息 首先无论是navicat 执行sql还是 用idea启动多的服务都会有如下报错信息&#xff1a; 2、解决办法 2.1命令方式修改 这种方法是由其他资料提供的。这种修改方式是临时的&#xff0c;如果mysql服务重启设置就会还原&#xff…

解决Python中使用requests库遇到的身份验证错误

在使用requests库进行HTTP请求时&#xff0c;用户遇到了一个AuthenticationRequired&#xff08;身份验证必须&#xff09;的错误。然而&#xff0c;当使用urllib.request.urlopen执行相同的操作时&#xff0c;却能够成功。同时&#xff0c;用户提供了自己的系统信息&#xff0…

Flink 整合 hudi

1、hudi介绍&#xff1a; Hudi 是一个开源的大数据存储和处理框架&#xff0c;通过提供数据表、写入、读取、更新和删除等功能&#xff0c;实现了高效的增量数据处理和数据管理。它广泛应用于大数据领域&#xff0c;为数据湖环境下的数据操作提供了强大的支持。不仅可以存储数…

D-阿强与网格

题目链接 : 阿强与网络 思路 : 数学模拟; 详情请看代码 : 代码 : #include<iostream> #include<algorithm> using namespace std; typedef long long LL; int main(){int t ; scanf("%d",&t);LL ans,m,n,x,y;while(t--){scanf("%lld%lld…

流量分析(5.5信息安全铁人三项赛数据赛题解)

黑客通过外部的web服务器攻击到企业内部的系统中&#xff0c;并留下了web后门&#xff0c;通过外部服务器对内部进行了攻击。 目录 黑客攻击的第一个受害主机的网卡IP地址 黑客对URL的哪一个参数实施了SQL注入 第一个受害主机网站数据库的表前缀(加上下划线 例如abc_) 第一…

win10资源管理器占用CPU过高导致卡顿

win10 打开几个文件夹后 资源管理器占用CPU 飙升&#xff0c;卡的很难受&#xff0c;网上找了几个办法 关闭 小娜&#xff0c;关闭搜索 什么的 都没明显改善&#xff0c;还有损招&#xff0c;重启资源管理器&#xff0c;重启一次 20多秒&#xff0c;要不了多长时间就会再次卡…

2023中国跨境电商出海成功品牌TOP5:跨境独立站

1 中国跨境电商出海最佳品牌 通过搭建跨境电商独立站&#xff0c;完善多渠道战略&#xff0c;并获取更多品牌自主权与更高利润率已是大势所趋。以下整理了外媒评选出的中国跨境电商出海最佳品牌&#xff08;指拥有“跨境电商独立站”并持有“自有品牌”的公司&#xff09;。本…

二维码智慧门牌管理系统升级解决方案:标准地址ID查询服务:高效、精准

文章目录 前言一、解决查询效率低下的问题二、提高信息精准度三、应用案例 前言 随着城市的发展和信息化步伐的加快&#xff0c;二维码智慧门牌管理系统已成为各大城市管理部门和企事业单位的必备工具。然而&#xff0c;实际应用中存在一些问题&#xff0c;如查询效率低下、信…

Sql Prompt 10下载安装图文教程

在操作过程中&#xff0c;请暂时关闭你的防病毒软件&#xff0c;以免其误报导致操作失败。 资源 SQL Prompt 10 https://www.aliyundrive.com/s/QuMWkvE1Sv6 点击链接保存&#xff0c;或者复制本段内容&#xff0c;打开「阿里云盘」APP &#xff0c;无需下载极速在线查看&…

web基础和http协议(粗糙版)

服务部署&#xff0c;集训&#xff0c;分布式&#xff0c;数据库&#xff0c;日志系统&#xff0c;等二阶段 web基础和http协议&#xff1a; web的相关基础知识&#xff0c;包括域名 dns解析 网页的概念以及http协议 1.网络当中通信&#xff1a;端口 ip 协议 tcp/ip 传输过程…

CPS-8910

PCI Express&#xff0c;有线开关设备 CPS-8910专为在PXI平台或软件无线电设备上实现大型多输入多输出(MIMO)扩展配置和系统控制而设计。 CPS-8910提供了2个PCI Express上行端口和8个下行端口来实现无缝系统扩展。 下行端口可以连接软件无线电可重配置设备等外部设备&#xff0…

抖音直播招聘报白企业人力资源有招聘需求的看过来

人力资源行业抖音招聘报白开始了&#xff0c;但是目前的市面的价格不一&#xff0c;很多人力资源公司最近想做抖音的直播报白&#xff0c;做直播待岗&#xff0c;因为最近刚好是招聘高峰期啊&#xff0c;企业需求大&#xff0c;赶上这一波&#xff0c;但是对目前市面上做抖音报…

前后端设置跨域问题

前端 const {defineConfig} require(vue/cli-service) module.exports defineConfig({transpileDependencies: true,devServer: { //记住&#xff0c;别写错了devServer//设置本地默认端口 选填port: 8080,proxy: { //设置代理&#xff0c;必…

振弦传感器钢筋计埋设与安装方法及注意要点

振弦传感器钢筋计埋设与安装方法及注意要点 振弦传感器钢筋计是一种常用于钢筋混凝土结构应变监测的传感器&#xff0c;其可以在钢筋受力时产生微小的振动信号&#xff0c;进而通过数据采集系统进行数据处理&#xff0c;得出钢筋受力状态的参数。在钢筋计的应用过程中&#xf…

揭秘拍卖竞价源码的前沿技术:加密、智能合约与更多

在数字时代的今天&#xff0c;拍卖竞价源码成为了炙手可热的话题。作为该领域的专家&#xff0c;我将带您深入了解这一前沿技术的奥秘。本文将揭示拍卖竞价源码的工作原理、加密技术的应用、智能合约的作用以及其他相关技术。 2. 拍卖竞价源码的工作原理 拍卖竞价源码是一种用…