代码随想录复习——单调栈篇 每日温度 下一个更大元素12 接雨水 柱状图中最大的矩形

739.每日温度

每日温度
暴力解法双指针

def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        n = len(temperatures)
        res = [0] * n
        for i in range(n):
            for j in range(i,n):
                if temperatures[j] <= temperatures[i]: continue
                else: 
                    res[i] = j-i
                    break
        return res

单调栈解法

那么接下来在来看看使用单调栈的解法。

那有同学就问了,我怎么能想到用单调栈呢? 什么时候用单调栈呢?

通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了。

时间复杂度为O(n)。

在使用单调栈的时候首先要明确如下几点:

单调栈里存放的元素是什么?
单调栈里只需要存放元素的下标i就可以了,如果需要使用对应的元素,直接T[i]就可以获取。

单调栈里元素是递增呢? 还是递减呢?
注意一下顺序为 从栈头到栈底的顺序,因为单纯的说从左到右或者从前到后,不说栈头朝哪个方向的话,大家一定会越看越懵。

这里我们要使用递增循序(再强调一下是指从栈头到栈底的顺序),因为只有递增的时候,加入一个元素i,才知道栈顶元素在数组中右面第一个比栈顶元素大的元素是i。

文字描述理解起来有点费劲,接下来我画了一系列的图,来讲解单调栈的工作过程。

使用单调栈主要有三个判断条件。

当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况
把这三种情况分析清楚了,也就理解透彻了。

class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        n = len(temperatures)
        res = [0] * n
        stack = [0]
        for i in range(1, n):
            if temperatures[i] <= temperatures[stack[-1]]:
                stack.append(i)
            else:
                while stack and temperatures[i] > temperatures[stack[-1]]:
                    res[stack[-1]] = i - stack[-1]
                    stack.pop()
                stack.append(i)
        return res

496.下一个更大元素 I

下一个更大的元素1
这道题和上一道题基本是一样的,找的都是下一个更大的元素
单调栈里的顺序都是递增的顺序,有两个区别,1是上一题找的是数组下标这道题找的是数值,2是这道题找的是nums1中的元素在nums2中对应的数字的下一个更大的

我们的res数组要开辟成nums1的大小,在遍历nums2的过程中,我们要判断nums2[i]是否在nums1中出现过,因为最后是要根据nums1元素的下标来更新result数组。

具体来说,我们还是比较每个元素和栈顶元素的大小,要维护一个递增的单调栈,小于等于栈顶的元素我们都直接把它们入栈,直到遇到大于栈顶的元素

当遇到大于栈顶的元素时,如果栈顶元素也在nums1中,那么我们就可以更新res数组里对应位置的值了;我们需要获取一下该栈顶元素在nums1的索引,此索引也就是它在res中的对应的位置,然后更新它的res值

当遇到大于栈顶的元素时,我们需要把栈顶pop出来,再比较下一个栈顶,如果依然是当前元素大于它的话,我们就一直pop,这也是为什么用 while stack and nums[i] > stack[-1]的原因;直到栈里没有比当前元素大的了,我们把当前元素入栈

def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
        m, n = len(nums1), len(nums2)
        res = [-1] * m
        stack = [nums2[0]]

        for i in range(1, n):
            if nums2[i] <= stack[-1]:
                stack.append(nums2[i])
            else:
                while stack and nums2[i] > stack[-1]:
                    if stack[-1] in nums1:
                        index = nums1.index(stack[-1])
                        res[index] = nums2[i]
                    stack.pop()
                stack.append(nums2[i])
        return res

503.下一个更大元素II

下一个更大元素2
这道题的难点就是在于,大小的比较可以循环,从左边再比较
注意,可不是仅最后一个元素可以从数组的左边开始再比较,是所有的数(除了第一个)都可以从数组左边再比较

因此,一定需要把数组拼成两倍长,然后再用单调栈去做
res数组还是单倍长,更新位置的话用i%n 单倍长就行了

总结
如何处理循环数组。
相信不少同学看到这道题,就想那我直接把两个数组拼接在一起,然后使用单调栈求下一个最大值不就行了!
确实可以!
把两个nums数组拼接在一起,使用单调栈计算出每一个元素的下一个最大值,就是答案

def nextGreaterElements(self, nums: List[int]) -> List[int]:
        n = len(nums)
        res = [-1] * n 
        stack = [0]

        for i in range(1, n*2):
            while stack and nums[i%n] > nums[stack[-1]]:
                res[stack[-1]] = nums[i%n]    
                stack.pop()
            stack.append(i%n)
            
        return res

42.接雨水

接雨水
1.单调栈解法

def trap(self, height: List[int]) -> int:
        n = len(height)
        stack = [0]
        sum_ = 0

        for i in range(1,n):
            if height[i] < height[stack[-1]]:
                stack.append(i)
            elif height[i] == height[stack[-1]]:
                stack.pop()
                stack.append(i)
            else:
                while stack and height[i] > height[stack[-1]]:
                    mid = stack[-1]
                    stack.pop()
                    if stack:
                        h = min(height[stack[-1]], height[i]) - height[mid]
                        w = i - stack[-1] - 1
                        sum_ += h*w
                stack.append(i)
        return sum_

2.双指针解法

首先双指针我们要明白是按行计算还是按列计算,按列计算好理解

如果按照列来计算的话,宽度一定是1了,我们再把每一列的雨水的高度求出来就可以了
首先第一列和最后一列是不参与运算的,记得跳过

每一列雨水的高度,取决于,该列 左侧最高的柱子和右侧最高的柱子中最矮的那个柱子的高度。即 min(lheight, rheight) 如求列4的雨水

列4左侧最高是2,右侧最高高3,两者取min,是2;(2-1) * 1就是它接雨水的量

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

首先从头遍历所有的列,并且要注意第一个柱子和最后一个柱子不接雨水,代码如下:
时间复杂度O(n), 力扣会超时

def trap(self, height: List[int]) -> int:
        n = len(height)
        sum = 0
        for i in range(n):
            if i == 0 or i == n-1: continue
            lheigh, riheigh = height[i], height[i]
            for l in range(i-1, -1, -1):
                if height[l] > lheigh: lheigh = height[l]
            for r in range(i+1, n):
                if height[r] > riheigh: riheigh = height[r]
            h = min(lheigh, riheigh) - height[i]
            if h > 0: sum += h*1
        return sum 

3.dp解法
在上面的双指针解法中,我们其实能感受到,在计算左边最大高度和右边最大高度的时候,有很多重复的运算

我们可以用dp数组把每一个位置的左边最大高度和右边最大的高度记录下来,这样就起到拿空间换时间的作用了;那么怎么去得到这样两个数组呢

从左向右遍历,记录一下当前位置左边最大高度,当前位置就变成下一个位置的左边最大高度了

maxLeft[0] = height[0];
        for (int i = 1; i < size; i++) {
            maxLeft[i] = max(height[i], maxLeft[i - 1]);

同理,从右向左遍历

maxRight[size - 1] = height[size - 1];
        for (int i = size - 2; i >= 0; i--) {
            maxRight[i] = max(height[i], maxRight[i + 1]);

总代码

def trap(self, height: List[int]) -> int:
        n = len(height)
        leftheight, rightheight = [0] * n, [0] * n
        
        leftheight[0]=height[0]
        for i in range(1,len(height)):
            leftheight[i]=max(leftheight[i-1],height[i])
        rightheight[-1]=height[-1]
        for i in range(len(height)-2,-1,-1):
            rightheight[i]=max(rightheight[i+1],height[i])
        
        result = 0
        for i in range(0,len(height)):
            h = min(leftheight[i],rightheight[i])-height[i]
            if h > 0: result += h
        return result

84.柱状图中最大的矩形

柱状图中最大的矩形
单调栈

这道题和接雨水是呼应的,接雨水找的是两边第一个大于它高度的柱子,这道题是找两边第一个小于它高度的下标

这就导致在这题中单调栈的顺序是从大到小的
在这里插入图片描述
此时大家应该可以发现其实就是栈顶和栈顶的下一个元素以及要入栈的三个元素组成了我们要求最大面积的高度和宽度

思路
单调栈的思路还是,当前遍历到的位置i,如果height[i] > height[stack[-1]], 我们直接入栈

如果说 height[i] == height[stack[-1]], 我们pop出那个,入栈新的,因为我们要的是坐标嘛

然后,遇到小于栈顶的情况的话,height[i] < height[stack[-1]], 我们开始更新我们的结果

怎么计算呢?还是老三样,栈顶作为mid坐标,是拿来用height[mid]作高度的,然后pop出来。此时的新栈顶作为leftidx, i 是rightidx,这俩是用来做宽度的

与接雨水不同的是,这道题的首尾两个柱子都可以作为核心柱子,做最大面积的尝试

所以为此所做的操作是,输入数组首尾各补上一个0
但是遍历的时候,还是栈先入0, 然后i从1到 len(new_heights), 这样才能确保边缘两根做主心骨的情况

def largestRectangleArea(self, heights: List[int]) -> int:
        stack = [0]
        res = 0
        heights = [0] + heights + [0]
        
        for i in range(1, len(heights)):
            if heights[i] > heights[stack[-1]]:
                stack.append(i)
            elif heights[i] == heights[stack[-1]]:
                stack.pop()
                stack.append(i)
            else:
                while stack and heights[i] < heights[stack[-1]]:
                    mid = stack[-1]
                    stack.pop()
                    if stack:
                        h = heights[mid]
                        w = i - stack[-1] - 1
                        res = max(res, h*w)
                stack.append(i)
        return res

2.dp思路
dp 普通思路
先说一下思路,计算公式是什么捏?遍历每一块矩形,它对应的最大面积就是heights[i] * (左边第一个小于它高度的坐标 - 右边第一个小于它高度的坐标 - 1)
然后动态更新一下这个res就行了

难就难在本题要记录每个柱子 左边第一个小于该柱子的下标,而不是左边第一个小于该柱子的高度。

所以需要循环查找,也就是下面在寻找的过程中使用了while,详细请看下面注释

def largestRectangleArea(self, heights: List[int]) -> int:
        n = len(heights)
        dpl, dpr = [0] * n, [0] * n
        res = 0

        # 记录每个柱子的左侧第一个矮一级的柱子的下标
        dpl[0] = -1
        for i in range(1, n):
            temp = i-1
            #当左边持续较高,尝试再左边的,直到找到第一个矮一点的柱子
            while temp >= 0 and heights[temp] >= heights[i]:
                temp = dpl[temp]
            #跳出这个循环说明有可能找到了
            dpl[i] = temp #把下标赋给当前的dp[i]
        
        dpr[n-1] = n
        for i in range(n-2, -1, -1):
            temp = i + 1
            while temp <= n-1 and heights[temp] >= heights[i]:
                temp = dpr[temp]
            dpr[i] = temp

        for i in range(n):
            area = heights[i] * (dpr[i] - dpl[i] -1)
            res = max(res, area)
        return res

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

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

相关文章

pytorch 计算混淆矩阵

混淆矩阵是评估模型结果的一种指标 用来判断分类模型的好坏 预测对了 为对角线 还可以通过矩阵的上下角发现哪些容易出错 从这个 矩阵出发 可以得到 acc &#xff01; precision recall 特异度&#xff1f; 目标检测01笔记AP mAP recall precision是什么 查全率是什么 查准率…

【K8S系列】深入解析Pod对象(一)

目录 序言 1.问题引入 1.1 问题描述 2 问题解答 2.1 pod 属性 2.1.1 NodeSelector 2.1.2 HostAliases 2.1.3 shareProcessNamespace 2.1.4 NodeName 2.1.5 其他pod属性 2.2 容器属性 2.2.1 ImagePullPolicy 2.2.2 Lifecycle 3 总结 4. 投票 序言 任何一件事情&am…

一文读懂强化学习!

一.了解强化学习1.1基本概念强化学习是考虑智能体&#xff08;Agent&#xff09;与环境&#xff08;Environment&#xff09;的交互问题&#xff1a;智能体处在一个环境中&#xff0c;每个状态为智能体对当前环境的感知&#xff1b;智能体只能通过动作来影响环境&#xff0c;当…

空间信息智能应用团队研究成果介绍及人才引进

目录1、多平台移动测量技术1.1 车载移动测量系统1.2 机载移动测量系统2、数据处理与应用技术研究2.1 点云与影像融合2.2 点云配准与拼接2.3 点云滤波与分类2.4 道路矢量地图提取2.5 道路三维自动建模2.6 道路路面三维病害分析2.7 多期点云三维变形分析2.8 地表覆盖遥感监测分析…

ChatGPT在安全研究领域的应用实践

引言ChatGPT是一个人工智能技术驱动的自然语言处理工具&#xff0c;它能够通过理解和学习人类的语言来进行对话&#xff0c;并能进行连续对话。目前ChatGPT已经官方已经更新模型到4.0版本&#xff0c;宣称它是“最先进的系统&#xff0c;能生产更安全和更有用的回复”。当前使用…

wandb:可视化和超参数寻优

参考博客&#xff1a;https://zhuanlan.zhihu.com/p/591047340 1、注册账号 首先&#xff0c;去wandb官网注册一个账号&#xff0c;选择个人使用即可&#xff08;根据个人需要&#xff09; 然后&#xff0c;登录得到一个API key 2、wandb使用 &#xff08;1&#xff09;命令…

Spring框架学习--xml和Annotation方式实现IOC

AnnotationXml的spring-IOC和全Annotation的spring-IOC 文章目录AnnotationXml的spring-IOC和全Annotation的spring-IOC学习目标第二章 基于AnnotationXml的spring-IOC【重点】1、annotationxml【入门案例】(5)【1】目标【2】实现【2.1】创建项目【2.3】改写AccountDaoImpl【2.…

刷题记录(2023.3.14 - 2023.3.18)

[第五空间 2021]EasyCleanup 临时文件包含考点 分析源码&#xff0c;两个特殊的点&#xff0c;一个是 eval&#xff0c;另一个是 include eval 经过了 strlen filter checkNums 三个函数 include 经过了 strlen filter 两个函数 filter 检测是否包含特定的关键字或字符 fun…

【数据结构与算法】用栈实现队列

文章目录&#x1f63b;前言如何用栈实现队列&#xff1f;用栈实现队列整体的实现代码&#x1f63c;写在最后&#x1f63b;前言 &#x1f61d;上一章我们用队列实现了一个栈&#xff08;-> 传送门 <-&#xff09;&#xff0c;而这一章就带大家用栈实现一个队列。 &#x1…

< 每日算法:在排序数组中查找元素的第一个和最后一个位置 >

每日算法 - JavaScript解析&#xff1a;在排序数组中查找元素的第一个和最后一个位置 一、任务描述&#xff1a;> 示例 1> 示例 2> 示例 3二、题意解析三、解决方案&#xff1a;往期内容 &#x1f4a8;一、任务描述&#xff1a; 给你一个按照非递减顺序排列的整数数组…

C++基础算法③——排序算法(选择、冒泡附完整代码)

排序算法 1、选择排序 2、冒泡排序 1、选择排序 基本思想&#xff1a;从头至尾扫描序列&#xff0c;每一趟从待排序元素中找出最小(最大)的一个元素值&#xff0c;然后与第一个元素交换值&#xff0c;接着从剩下的元素中继续这种选择和交换方式&#xff0c;最终得到一个有序…

冲击蓝桥杯-时间问题(必考)

目录 前言&#xff1a; 一、时间问题 二、使用步骤 1、考察小时&#xff0c;分以及秒的使用、 2、判断日期是否合法 3、遍历日期 4、推算星期几 总结 前言&#xff1a; 时间问题可以说是蓝桥杯&#xff0c;最喜欢考的问题了,因为时间问题不涉及到算法和一些复杂的知识&#xf…

第十四届蓝桥杯三月真题刷题训练——第 18 天

目录 第 1 题&#xff1a;排列字母 问题描述 运行限制 代码&#xff1a; 第 2 题&#xff1a;GCD_数论 问题描述 输入格式 输出格式 样例输入 样例输出 评测用例规模与约定 运行限制 第 3 题&#xff1a;选数异或 第 4 题&#xff1a;背包与魔法 第 1 题&#x…

1649_Excel中删除重复的数据

全部学习汇总&#xff1a; GreyZhang/windows_skills: some skills when using windows system. (github.com) 长久时间的开发工作性质导致我对Windows生态下的很多工具没有一个深度的掌握。有时候&#xff0c;别说深度&#xff0c;可能最为浅显的操作都是不熟悉的。这个不仅仅…

JVM学习.02 内存分配和回收策略

1、前言《JVM学习.01 内存模型》篇讲述了JVM的内存布局&#xff0c;其中每个区域是作用&#xff0c;以及创建实例对象的时候内存区域的工作流程。上文还讲到了关于对象存货后&#xff0c;会被回收清理的过程。今天这里就着重讲一下对象实例是如何被清理回收的&#xff0c;以及清…

5.方法(最全C#方法攻略)

目录 5.1 方法的结构 5.2 方法体内部的代码执行 5.3.1 类型推断和Var关键字 5.3.2 嵌套块中的本地变量 5.4 本地常量 5.5 控制流 5.6 方法调用 5.7 返回值 5.8 返回语句和void 方法 5.9 参数 5.9.1 形参 5.9.2 实参 位置参数示例 5.10 值参数 5.11 引用参数 5.12…

【vm虚拟机】vmware虚拟机下载安装

vmware虚拟机下载安装&#x1f6a9; vmware虚拟机下载&#x1f6a9; 安装虚拟机程序&#x1f6a9; 创建一个CentOS虚拟机&#x1f6a9; 异常情况&#x1f6a9; vmware虚拟机下载 vmware官网下载地址 &#x1f6a9; 安装虚拟机程序 双击安装包exe程序&#xff0c;无脑下一步即…

来到CSDN的一些感想

之所以会写下今天这篇博客&#xff0c;是因为心中实在是有很多话想说&#xff01;&#xff01;&#xff01; 认识我的人应该都知道&#xff0c;我是才来CSDN不久的&#xff0c;也可以很清楚地看见我的码龄&#xff0c;直到今天&#xff1a;清清楚楚地写着&#xff1a;134天&…

完美日记母公司再度携手中国妇基会,以“创美人生”助力女性成长

撰稿 | 多客 来源 | 贝多财经 当春时节&#xff0c;梦想花开。和煦的三月暖阳&#xff0c;唤醒的不止是满城春意&#xff0c;更有逸仙电商“创美人生”公益项目播撒的一份希望。 3月8日“国际妇女节”当日&#xff0c;为积极响应我国促进共同富裕的政策倡导&#xff0c;助力相…

C语言--自定义类型详解

目录结构体结构体的声明特殊的声明结构的自引用typedef的使用结构体变量的定义和初始化结构体的内存对齐为什么存在内存对齐&#xff1f;修改默认对齐数结构体传参位段位段的内存分配位段的跨平台问题枚举联合联合类型的定义联合在内存中开辟空间联合大小的计算结构体 结构体的…