代码随想录day59|503. 下一个更大元素 II|42. 接雨水|Golang

news2024/12/23 13:22:50

代码随想录day59

还剩下一天

目录

代码随想录day59

503. 下一个更大元素 II

42. 接雨水

双指针解法

动态规划解法

单调栈解法


503. 下一个更大元素 II

func nextGreaterElements(nums []int) []int {
    n := len(nums)
    ans := make([]int,n,n)
    for i:=0;i<len(ans);i++{
        ans[i] = -1
    }
    //单调递减,存储数组下标索引
    stack := make([]int,0)

    for i:=0;i<n*2;i++{
        for len(stack)>0&&nums[i%n]>nums[stack[len(stack)-1]]{
            top := stack[len(stack)-1]
            stack = stack[:len(stack)-1] // pop
            ans[top] = nums[i%n]
        }
        stack = append(stack,i%n)
    }
    return ans
}

42. 接雨水

思路

接雨水问题在面试中还是常见题目的,有必要好好讲一讲。

本文深度讲解如下三种方法:

  • 双指针法
  • 动态规划
  • 单调栈

双指针解法

这道题目使用双指针法并不简单,我们来看一下思路。

首先要明确,要按照行来计算,还是按照列来计算。

按照行来计算如图:

 按照列来计算如图:

一些同学在实现的时候,很容易一会按照行来计算一会按照列来计算,这样就会越写越乱。

        我个人倾向于按照列来计算,比较容易理解,接下来看一下按照列如何计算。

        首先,如果按照列来计算的话,宽度一定是1了,我们再把每一列的雨水的高度求出来就可以了。

        可以看出每一列雨水的高度,取决于,该列 左侧最高的柱子和右侧最高的柱子中最矮的那个柱子的高度。

        这句话可以有点绕,来举一个理解,例如求列4的雨水高度,如图:

// (当前能接的水为 左右两边柱子高度较小的减去当前位置的高度,即为能装水的高度)。

 

列4 左侧最高的柱子是列3,高度为2(以下用lHeight表示)。

列4 右侧最高的柱子是列7,高度为3(以下用rHeight表示)。

列4 柱子的高度为1(以下用height表示)

那么列4的雨水高度为 列3和列7的高度最小值减列4高度,即: min(lHeight, rHeight) - height。

列4的雨水高度求出来了,宽度为1,相乘就是列4的雨水体积了。

此时求出了列4的雨水体积。

一样的方法,只要从头遍历一遍所有的列,然后求出每一列雨水的体积,相加之后就是总雨水的体积了。

首先从头遍历所有的列,并且要注意第一个柱子和最后一个柱子不接雨水,代码如下:

for (int i = 0; i < height.size(); i++) {
    // 第一个柱子和最后一个柱子不接雨水
    if (i == 0 || i == height.size() - 1) continue;
}

在for循环中求左右两边最高柱子,代码如下:

int rHeight = height[i]; // 记录右边柱子的最高高度
int lHeight = height[i]; // 记录左边柱子的最高高度
for (int r = i + 1; r < height.size(); r++) {
    if (height[r] > rHeight) rHeight = height[r];
}
for (int l = i - 1; l >= 0; l--) {
    if (height[l] > lHeight) lHeight = height[l];
}

最后,计算该列的雨水高度,代码如下:

int h = min(lHeight, rHeight) - height[i];
if (h > 0) sum += h; // 注意只有h大于零的时候,在统计到总和中

整体代码如下:

func trap(height []int) int {
    var sum int
    for i:=0;i<len(height);i++{
        // 第一个柱子和最后一个柱子不接雨水
        if i==0 || i == len(height)-1 {
            continue
        }
        //在for循环中求左右两边最高柱子
        rHeight := height[i]    // 记录右边柱子的最高高度
        lHeight := height[i]    // 记录左边柱子的最高高度
        for r:=i+1;r<len(height);r++{
            if height[r] > rHeight {
                rHeight = height[r]
            }
        }
        for l:=i-1;l>=0;l--{
            if height[l] > lHeight {
                lHeight = height[l]
            }
        }

        // 当前能接的水为 左右两边柱子高度较小的减去当前位置的高度,即为能装水的高度。
        h := min(lHeight,rHeight) - height[i]
        fmt.Println("第",i,"次",lHeight,rHeight,height[i], "接的水h为:",h)
        if h>0 {
            sum += h
        }
    }
    return sum
}
func min(a, b int) int {
    if a < b {
        return a 
    }
    return b
}

// output
第 1 次 1 3 1 接的水h为: 0
第 2 次 1 3 0 接的水h为: 1
第 3 次 2 3 2 接的水h为: 0
第 4 次 2 3 1 接的水h为: 1
第 5 次 2 3 0 接的水h为: 2
第 6 次 2 3 1 接的水h为: 1
第 7 次 3 3 3 接的水h为: 0
第 8 次 3 2 2 接的水h为: 0
第 9 次 3 2 1 接的水h为: 1
第 10 次 3 2 2 接的水h为: 0

动态规划解法

        在上一节的双指针解法中,我们可以看到只要记录左边柱子的最高高度 和 右边柱子的最高高度,就可以计算当前位置的雨水面积,这就是通过列来计算。

        当前列雨水面积:min(左边柱子的最高高度,记录右边柱子的最高高度) - 当前柱子高度。

        为了得到两边的最高高度,使用了双指针来遍历,每到一个柱子都向两边遍历一遍,这其实是有重复计算的。我们把每一个位置的左边最高高度记录在一个数组上(maxLeft),右边最高高度记录在一个数组上(maxRight)。这样就避免了重复计算,这就用到了动态规划。

        当前位置,左边的最高高度是前一个位置的左边最高高度和本高度的最大值。

        即从左向右遍历:maxLeft[i] = max(height[i], maxLeft[i - 1]);

        从右向左遍历:maxRight[i] = max(height[i], maxRight[i + 1]);

这样就找到递推公式。

代码如下:

func trap(height []int) int {
    n := len(height)
    maxLeft := make([]int, n)
    maxRight := make([]int,n)

    // 记录每个柱子左边柱子最大高度
    maxLeft[0]=height[0]
    for i:=1;i<n;i++{
        maxLeft[i] = max(height[i], maxLeft[i-1])
    }
    // 记录每个柱子右边柱子最大高度
    maxRight[n-1] = height[n-1]
    for i:=n-2;i>=0;i--{
        maxRight[i] = max(height[i], maxRight[i+1])
    }
    // 求和
    sum :=0 
    for i:=0;i<n;i++{
        count := min(maxLeft[i], maxRight[i]) - height[i]
        if count > 0 {
            sum += count
        }
    }
    return sum
}
func max(a, b int) int {
    if a > b {
        return a 
    }
    return b
}
func min(a, b int) int {
    if a < b {
        return a 
    }
    return b
}

单调栈解法

        这个解法可以说是最不好理解的了,所以下面我花了大量的篇幅来介绍这种方法。

        单调栈就是保持栈内元素有序。和栈与队列:单调队列一样,需要我们自己维持顺序,没有现成的容器可以用

准备工作

那么本题使用单调栈有如下几个问题:

1、首先单调栈是按照行方向来计算雨水,如图:

知道这一点,后面的就可以理解了。

2、使用单调栈内元素的顺序

        从大到小还是从小到大呢?

        从栈头(元素从栈头弹出)到栈底的顺序应该是从小到大的顺序。

        因为一旦发现添加的柱子高度大于栈头元素了,此时就出现凹槽了,栈头元素就是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子。

如图:

 3、遇到相同的元素怎么办?

        遇到相同的元素,更新栈内下标,就是将栈里元素(旧下标)弹出,将新元素(新下标)加入栈中。

        例如 5 5 1 3 这种情况。如果添加第二个5的时候就应该将第一个5的下标弹出,把第二个5添加到栈中。

        因为我们要求宽度的时候 如果遇到相同高度的柱子,需要使用最右边的柱子来计算宽度

如图所示:

 4、栈里要保存什么值?

        使用单调栈,其实是通过 长 * 宽 来计算雨水面积的。        

        长就是通过柱子的高度来计算,宽是通过柱子之间的下标来计算,

        那么栈里有没有必要存一个pair<int, int>类型的元素,保存柱子的高度和下标呢。

        其实不用,栈里就存放int类型的元素就行了,表示下标,想要知道对应的高度,通过height[stack.top()] 就知道弹出的下标对应的高度了。

所以栈的定义如下:

stack<int> st; // 存着下标,计算的时候用下标对应的柱子高度

明确了如上几点,我们再来看处理逻辑。

单调栈处理逻辑

先将下标0的柱子加入到栈中,st.push(0);

然后开始从下标1开始遍历所有的柱子,for (int i = 1; i < height.size(); i++)

        如果当前遍历的元素(柱子)高度小于栈顶元素的高度,就把这个元素加入栈中,因为栈里本来就要保持从小到大的顺序(从栈头到栈底)。

代码如下:

if (height[i] < height[st.top()])  st.push(i);

        如果当前遍历的元素(柱子)高度等于栈顶元素的高度,要跟更新栈顶元素,因为遇到相相同高度的柱子,需要使用最右边的柱子来计算宽度。

代码如下:

if (height[i] == height[st.top()]) { // 例如 5 5 1 7 这种情况
  st.pop();
  st.push(i);
}

如果当前遍历的元素(柱子)高度大于栈顶元素的高度,此时就出现凹槽了,如图所示:

 

        取栈顶元素,将栈顶元素弹出,这个就是凹槽的底部,也就是中间位置,下标记为mid,对应的高度为height[mid](就是图中的高度1)。

此时的栈顶元素st.top(),就是凹槽的左边位置,下标为st.top(),对应的高度为height[st.top()](就是图中的高度2)。

当前遍历的元素i,就是凹槽右边的位置,下标为i,对应的高度为height[i](就是图中的高度3)。

此时大家应该可以发现其实就是栈顶和栈顶的下一个元素以及要入栈的三个元素来接水!

那么雨水高度是 min(凹槽左边高度, 凹槽右边高度) - 凹槽底部高度,代码为:int h = min(height[st.top()], height[i]) - height[mid];

雨水的宽度是 凹槽右边的下标 - 凹槽左边的下标 - 1(因为只求中间宽度),代码为:int w = i - st.top() - 1 ;

当前凹槽雨水的体积就是:h * w

求当前凹槽雨水的体积代码如下:

while (!st.empty() && height[i] > height[st.top()]) { // 注意这里是while,持续跟新栈顶元素
    int mid = st.top();
    st.pop();
    if (!st.empty()) {
        int h = min(height[st.top()], height[i]) - height[mid];
        int w = i - st.top() - 1; // 注意减一,只求中间宽度
        sum += h * w;
    }
}

关键逻辑都讲完了,完整的go代码如下:

func trap(height []int) int {
   if len(height) <= 2 {
      return 0
   }
   st := make([]int, 1, len(height)) // 切片模拟单调栈,st存储的是高度数组下标
   var res int
   for i := 1; i < len(height); i++ {
      if height[i] < height[st[len(st)-1]] {
         st = append(st, i)
      } else if height[i] == height[st[len(st)-1]] {
         st = st[:len(st)-1] // 比较的新元素和栈顶的元素相等,去掉栈中的,入栈新元素下标
         st = append(st, i)
      } else {
         for len(st) != 0 && height[i] > height[st[len(st)-1]] {
            top := st[len(st)-1]
            st = st[:len(st)-1]
            if len(st) != 0 {
               tmp := (min(height[i], height[st[len(st)-1]]) - height[top]) * (i - st[len(st)-1] - 1)
               res += tmp
            }
         }
         st = append(st, i)
      }
   }
   return res
}


func min(x, y int) int {
   if x >= y {
      return y
   }
   return x
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ECCV2022 商汤 发布最大的表征学习预训练数据集OmniBenchmark解读

近些年&#xff0c;基于深度模型的表征学习算法在某些知识域上&#xff08;例如人脸、动物等&#xff09;取得了非常优异的成绩&#xff0c;然而由于现有数据集覆盖的视觉类别仍然比较有限&#xff0c;一个覆盖视觉类别足够广&#xff0c;且能够支持学习到适用于许多视觉类别的…

CRM的定义是什么?这么多CRM产品,CRM客户管理系统该如何选择?

在禽流感的助涨下&#xff0c;CRM被拉到了众矢之的。 为甚么这样说呢&#xff1f;绝大多数民营企业遭遇着巨大的存活压力&#xff0c;导致民营企业不得已展开结构调整和网络化。CRM作为一种专精的客人关系管理工作工具&#xff0c;再次受到追捧。 CRM具有客人管理工作、网络营…

JavaScript的DOM技术

JavaScript的DOM技术 文章目录JavaScript的DOM技术1.DOM简介1.1 DOM概念2.获取元素2.1 如何获取页面元素2.2 根据ID获取2.3 根据标签名获取2.4 根据标签名获取2.5 通过H5新增方法获取2.6 获取特殊元素3.事件基础3.1 事件概述3.2 事件三要素3.3 执行事件的步骤3.4 鼠标事件4.操作…

工程施工监理平台app开发 开创工程监理服务新理念

工程项目最容易让人焦头烂额&#xff0c;很难统揽全局&#xff0c;更无法将施工中的每一个问题都处理得当&#xff0c;工程施工监理平台app开发&#xff0c;从根本上解决了工程监管问题&#xff0c;成为工程施工监管方面的福音和好帮手。工程施工监理平台app开发是专注工程现场…

【单目3D目标检测】FCOS3D + PGD论文解析与代码复现

文章目录前言FCOS3D概述主要创新点主要框架结构回归目标损失函数推理过程2D引导的多层3D预测2D高斯分布的3D中心度实验设置源码复现PGD概述主要创新点深度估计主要框架结构创新点一&#xff1a;概率表示的不确定性建模DPD_PDP​创新点二&#xff1a;透视几何体的深度传播DGD_GD…

删除或者移动文件/文件夹时,提示:文件/文件夹正在使用

问题 有时候我们在移动或者删除文件/文件夹时&#xff0c;系统会提示“文件正在使用”。 操作无法完成&#xff0c;因为其中的文件夹或者文件已经在另一程序中打开 请关闭该文件夹或文件&#xff0c;然后重试。 这是因为文件夹中的某个文件被打开了&#xff0c;或者该文件或文件…

VR云游带你玩转智慧文旅,解决景区营销痛点

有人说防控措施正在逐步放开&#xff0c;大家那颗热爱旅游的心是不是正在蠢蠢欲动了呢&#xff1f;不要急&#xff0c;先来一波VR云游助助兴吧&#xff01; 朝游青山暮游雪&#xff0c; 上午还在十里长湖、八里磨山&#xff0c; 下午便在毓秀金陵、钟山龙蟠&#xff0c; 看大…

[附源码]SSM计算机毕业设计超市订单管理系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

前端使用nginx部署项目到服务器

目录 一、下载nginx 二、启动 三、验证 四、windows的操作指南 五、目录介绍 六、部署 一、下载nginx 下载地址&#xff1a;https://nginx.org/en/download.html 二、启动 两种方法&#xff1a; 1&#xff09; 直接双击该目录下的"nginx.exe"&#xff0c;即…

设置代理服务器

挂载代理 前提&#xff1a; 同一个局域网&#xff0c;有一个IP白名单可以访问网络&#xff08;win10&#xff09;。想实现其他机器&#xff08;linux&#xff09;共同访问。 先将机器(linux)设置固定IP&#xff0c;同一网关下。静态IP设置&#xff0c;保证能PING通。将win机器…

雪佛兰畅巡新能源电动汽车CANBUS总线适配及汽车远程控制车联网系统

随着智能交通的发展&#xff0c;中国作为全球最大的汽车市场&#xff0c;车联网的市场容量巨大&#xff0c;国内车联网摸索了很多年&#xff0c;前装也还是属于霸屏&#xff0c;提供信息娱乐等初期阶段。互联网对汽车行业确实推动了不少&#xff0c;汽车公司也与互联网融合做过…

淘宝天猫CTO若海:沉浸式的消费体验是下一步发力方向

​每年双 11 开卖的那一刻&#xff0c;千万用户同时在线下单&#xff0c;那个瞬间服务器的压力是平时流量的数百倍&#xff0c;淘宝系统是否能够稳定支撑&#xff0c;是每年所有人关注的热点话题。 时至今日&#xff0c;丝般顺滑已经逐渐成为稳态。从今年开始&#xff0c;双 11…

使用minio进行文件存储

title: 使用Minio存储文件对象 一. Docker拉取镜像&#xff08;确保自己的服务器已经安装Docker&#xff09; docker pull minio/minio二. 启动一个miniio容器 docker run --name minio -p 9090:9000 -p 9999:9999 -d \ --restartalways -e \ "MINIO_ROOT_USERminio&qu…

“码二代”从喜欢益智游戏到找最短路线,编程思维是如何培养的?

前言 1842年&#xff0c;“数字女王”的阿达洛芙莱斯&#xff08;Ada Lovelace&#xff09;编写了历史上首款电脑程序&#xff0c;至今已有200多年的历史。 &#xff08;文末送读者福利&#xff09; 在这个特别的日子里&#xff0c;我们要为大家介绍的是一个来自小小“码二代…

线性表详细讲述(带图)

文章目录线性表---顺序表和链表1.线性表2.顺序表2.1概念2.2 静态顺序表与动态顺序表2.3接口的实现2.3.1顺表的初始化2.3.2扩容2.3.3顺序表尾插2.3.4顺序表的尾删2.3.5顺序表的头插2.3.6顺序表的头删2.3.7顺序表的查找2.3.8顺序表的任意位置插入2.3.9顺序表的任意位置删除2.3.10…

[附源码]java毕业设计民宿网站管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【DL】linux服务器上安装Anaconda3

1.本地连接远程服务器 使用MobaXterm连接远程服务器 2.下载Anaconda3安装包 安装包下载地址 https://www.anaconda.com/ 因为我们要在linux服务器上安装,因此选择linux安装包 下载完成以后,将安装包拖进服务器 3.安装Anaconda3 打开终端,输入以下命令,目的是赋权限 c…

Vue路由

参考文献&#xff1a;Vue中的路由 一、路由理解&#xff1a; 一个路由就是一组映射关系&#xff08;key&#xff0c;value&#xff09;&#xff0c;多个路由需要路由器&#xff08;router&#xff09;进行管理。其中key是路径&#xff0c;value是组件。作用&#xff1a;设定访…

【C++笔试强训】第二十六天

&#x1f387;C笔试强训 博客主页&#xff1a;一起去看日落吗分享博主的C刷题日常&#xff0c;大家一起学习博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a;夜色难免微凉&#xff0c;前方必有曙光 &#x1f31e;。 &#x1f4a6;&a…

springboot security 集成 cas 问题 No subject alternative names present

springboot security 集成 cas 问题 No subject alternative names present前言一、问题1.实际问题二、大海捞针1.星星之火2.通用方法啰嗦一句解决2.新建三个类配置文件修改前言 场景&#xff1a; 在一次springboot security 集成 cas开发中&#xff0c;代码报错&#xff1a;j…