代码随想录算法训练营Day59 | 503. 下一个更大元素II | 42. 接雨水

news2024/9/23 23:32:08

文章目录

  • 503. 下一个更大元素II
  • 42. 接雨水
    • 暴力解法
    • 双指针优化
    • 单调栈
      • 单调栈的处理逻辑

503. 下一个更大元素II

题目链接 | 解题思路

本题和每日温度非常相似,只是需要循环数组。最简单的方法当然是直接拼接数组,然后直接使用单调栈,最后修改输出的形状即可。不过这个方法需要修改数组,有额外的空间、时间复杂度。

以下可以直接模拟走两遍数组的过程,而不需要额外的复杂度。

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

        for i in range(1, len(nums) * 2):
            if nums[i % len(nums)] <= nums[stack[-1]]:
                stack.append(i % len(nums))
            else:
                while (len(stack) > 0 and nums[i % len(nums)] > nums[stack[-1]]):
                    result[stack[-1]] = nums[i % len(nums)]
                    stack.pop()
                stack.append(i % len(nums))
        return result

42. 接雨水

题目链接 | 解题思路

本题真的很经典、很常考!不同解法考验了不同方面的知识,真乃神题!

在开始选择解法之前,一定要先理清思路:究竟是按行还是按列来进行计算?

暴力解法

暴力解法实际上也是双指针。按照列来计算比较容易,此时只需要计算每一列中能够储存的雨水高度即可。每一列的雨水高度取决于

  • 这一列左边最高的柱子高度
  • 这一列右边最高的柱子高度

(补一张图!)

得到两者中更小的高度后,减去当前列的柱子高度就是雨水高度.
想清楚思路后很容易写出代码,要注意的是最左侧和最右侧的柱子是没办法接雨水的。

class Solution:
    def trap(self, height: List[int]) -> int:
        sum = 0
        for i in range(1, len(height) - 1):
            left_max_height = right_max_height = height[i]
            for left in range(i-1, -1, -1):
                if height[left] > left_max_height:
                    left_max_height = height[left]
            for right in range(i+1, len(height)):
                if height[right] > right_max_height:
                    right_max_height = height[right]
            sum += min(left_max_height, right_max_height) - height[i]
        return sum

意料之中,暴力解法的时间复杂度是 O ( n 2 ) O(n^2) O(n2),会超时。

双指针优化

暴力解法中有大量的重复计算:在计算每一列作为谷底的时候,都要进行一次遍历来得到左侧的最大高度和右侧的最大高度。

优化:可以通过两个静态的数组来分别记录数组中每个位置的左侧最大高度、右侧最大高度,只要以 O ( n ) O(n) O(n) 的时间完成这两个记录,就能优化整体的时间复杂度。
实际的记录过程有点类似于简单的 dp。以左侧最大高度为例,

  • 如果当前列的高度小于左侧列的左侧最大高度,则 left_higher[i] = left_higher[i-1]
  • 否则,当前列左侧没有比自己更高的,当前列的左侧最大高度即是自己,left_higher[i] = height[i]
class Solution:
    def trap(self, height: List[int]) -> int:
        left_higher = [0] * len(height)
        left_higher[0] = height[0]
        for i in range(1, len(height)):
            left_higher[i] = max(left_higher[i-1], height[i])

        right_higher = [0] * len(height)
        right_higher[-1] = height[-1]
        for i in range(len(height) - 2, -1, -1):
            right_higher[i] = max(right_higher[i+1], height[i])
        
        sum = 0
        for i in range(1, len(height) - 1):
            sum += min(left_higher[i], right_higher[i]) - height[i]

        return sum

空间换时间的优化,此时的时间复杂度是 O ( n ) O(n) O(n),但是空间复杂度也是 O ( n ) O(n) O(n),因为记录了两个数组。

单调栈

单调栈在本题中的应用思路有些模糊。一方面,单调栈适用于寻找当前元素左侧/右侧的第一个更大元素值,和本题的“寻找谷地”有紧密的联系;另一方面,之前在思路中提到的是寻找“左侧/右侧的最大高度”,而不是第一个更大元素,这似乎又没有很紧密的关系。

思路转换的突破口在于,要使用单调栈解题,应该按行计算

  1. 按照计算,就需要找到这一行(一个谷内的行,不是传统意义上的一行)的起始位置。而起始位置就是由这一行的左、右侧第一个更大值的位置决定的。

  2. 单调栈内的元素顺序、如何取值

    • 栈内的顺序应该是 top-bottom 递增,这样向右搜索的时候,如果当前元素值大于栈口元素值,能够找到栈口元素的右侧第一个更大值,也就知道当前行(栈口元素代表的那一行)的右边界
    • 栈口元素的内部第一个元素值是栈口元素左侧的第一个更大值,也就是当前行的左边界
  3. 当前元素与栈口元素相等:这个情况不像之前的题目一样直接,需要进行额外的讨论

    • 当遇到高度和栈口元素相等的柱子时,最好将栈口元素(i.e. 下标)弹出,然后压入当前元素(i.e. 下标)。这是因为求当前行的宽度时,需要用最右侧的柱子下标进行计算

    • 当然,注意到上面的用词是“最好”。如果不进行弹出,而是只将当前元素压入,不会影响最后的结果,但是后续会额外做一些雨水量为 0 的计算。

  4. 栈内记录的元素:通过单调栈的更新结果来计算雨水量,需要当前行的高和宽。其中高需要列的值,宽需要列的下标,既然通过下标可以直接获取值,栈内只需要记录下标即可。

单调栈的处理逻辑

单调栈的三种情况:

  1. 当前的列高度小于栈口元素的高度 height[i] < height[stack[-1]]
    • 将当前的元素压入栈中,更新当前行的位置,同时还没有找到谷的右边界
  2. 当前的列高度等于栈口元素的高度 height[i] == height[stack[-1]]
    • optional:将当前栈口元素弹出
    • 仍然在搜索当前行,需要更新当前行的最右侧,保持当前行的高度
  3. 当前的列高度大于栈口元素的高度 height[i] > height[stack[-1]]
    • 当前元素即为谷的右边界
    • 将栈口元素弹出作为谷底,此时新的栈口元素可以视作是谷的左边界
    • 得到了谷的左右边界,以及谷底高度,就可以轻松计算出这个谷的接雨水量(注意这是一个横向的计算!)
      • 如果在处理 height[i] == height[stack[-1]] 时没有弹出之前的栈口元素,那么可能会遇到谷内能够接到的雨水量为 0 的情况
class Solution:
    def trap(self, height: List[int]) -> int:
        sum = 0
        stack = [0]

        for i in range(1, len(height)):
            if height[i] < height[stack[-1]]:
                stack.append(i)
            elif height[i] == height[stack[-1]]:
                stack.pop()         # optional, improve performances
                stack.append(i)
            else:
                while (len(stack) > 0 and height[i] > height[stack[-1]]):
                    valley_bottom = stack.pop()     # store it as it is popped
                    if len(stack) > 0:      # possibly empty now
                        curr_height = min(height[stack[-1]], height[i]) - height[valley_bottom]  
                        curr_width = i - stack[-1] - 1
                        sum += curr_height * curr_width
                stack.append(i)
        return sum

单调栈的解法应该是最优的,时间复杂度是 O ( n ) O(n) O(n),空间复杂度也是 O ( n ) O(n) O(n),并且比双指针 + dp 少记录了一个数组。

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

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

相关文章

搭建Jaeger

本篇是对 Golang 上手GORM V2 Opentracing链路追踪优化CRUD体验&#xff08;源码阅读&#xff09;[1] 阅读与实践 该篇相关代码[2] GORM V2版本开始支持Context上下文传递&#xff0c;支持插件Plugins(有了插件&#xff0c;callback和hook的代码就能更优雅一点) ORM利用反射&a…

Seata1.5.2解决分布式事务问题

分布式事务–Seata ​ 前面了解到一些分布式事务的解决方案&#xff0c;业内也涌现出不少解决分布式事务的优秀框架&#xff0c;如Atomikos、Seata等&#xff0c;本章来了解使用下Seata。 ​ Seata的前身是Fescar&#xff0c;而后改名Seata&#xff0c;简单可扩展的自治分布式…

性能工程全景图、实施方案、建设目标

性能工程是一个关注系统性能层面的体系,包含测试环境的性能测试、生产环境的性能测试、性能调优、容量规划等多个方面 性能工程主要包括如下3个部分 ❑建设性能测试理论体系和流程规范、链路分析基础知识体系和流程规范、性能调优基础理论体系和流程规范。 ❑搭建高效协同的工…

js通过xpath定位元素并且操作元素以下拉框select为例

js也可以使用xpath定位元素&#xff0c;现在实例讲解。 页面上有一个下拉框&#xff0c;里面内容有三个&#xff0c;用F12看一下 一、使用xpath定位这个下拉框select eldocument.evaluate(//select[name"shoppingPreference"], document).iterateNext()二、为下拉框…

代码随想录算法训练营Day60 | 84. 柱状图中最大的矩形

文章目录 84. 柱状图中最大的矩形首尾加 0双指针 84. 柱状图中最大的矩形 题目链接 | 解题思路 本题和接雨水的题目相互呼应&#xff0c;但是难度略有提升&#xff0c;同样是一道非常棒的题&#xff01; 在接雨水中&#xff0c;需要找到每一列的左侧最大值和右侧最大值&…

高阶数据结构(2)-----红黑树

一)红黑树的基本概念和基本性质: 1)红黑树就是一种高度平衡的二叉搜索树&#xff0c;但是在每一个节点上面都增加了一个存储位来表示结点的颜色&#xff0c;可以是红色或者是黑色&#xff0c;通过对任何一条从根节点到叶子节点上面的路径各个节点着色方式的限制&#xff0c;红黑…

Selenium+Pytest自动化测试框架实战(下)

前言 本文接上篇文章哟。 一、简单学习元素定位 在日常的工作中&#xff0c;我见过很多在浏览器中直接在浏览器中右键Copy Xpath复制元素的同学。这样获得的元素表达式放在 webdriver 中去运行往往是不够稳定的&#xff0c;像前端的一些微小改动&#xff0c;都会引起元素无法…

Truenas scale 安装 Tailscale 内网穿透远程连接SMB服务

起源 没有公网IP&#xff0c;不在同一个路由器&#xff0c;没法远程连接电脑或者服务器。之前一直使用 zerotier&#xff0c; 但是这次使用 Truenas scale 安装 zerotier&#xff0c; 每次重启都变化IP&#xff0c;一直没解决。转投 tailscale。 注册 YouTube有教程&#xf…

基于ssm智能停车场031

大家好✌&#xff01;我是CZ淡陌。一名专注以理论为基础实战为主的技术博主&#xff0c;将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路…

安装并设置linux虚拟机ubuntu20.04.6 LTS

1 安装虚拟机 1、安装虚拟机模拟软件virtualboxhttps://download.virtualbox.org/virtualboxhttps://download.virtualbox.org/virtualbox/7.0.10/VirtualBox-7.0.10-158379-Win.exe 2、在virtualbox中安装虚拟机ubuntu 20.04.6 LTS 桌面版https://www.releases.ubuntu.com/fo…

进度条--QProgressBar,进度对话框--QProgressDialog

一、QProgressBar 进度条 1、QProgressBar 类继承自 QWidget&#xff0c;它是一个 QWidget 部件&#xff0c; QProgressBar 除了将其放置 于进度对话框之中外&#xff0c;还可将其放置于窗口的状态栏等其他部件中。 QProgressBar提供了一个水平或垂直的进度条&#xff0c;可以…

JDBC基本概念

什么是JDBC JDBC概念 JDBC&#xff08;Java DataBase Connectivity&#xff09;是一套统一的基于Java语言的关系数据库编程接口规范。 该规范允许将SQL语句作为参数通过JDBC接口发送给远端数据库&#xff0c; …

8位和32位单片机如何选择适合,以及主要区别!

单片机直接影响到项目的成功和性能&#xff0c;我们将分享如何选择适合您的应用的8位或32位单片机。 8位单片机 vs. 32位单片机&#xff1a; 一、性能和处理能力&#xff1a; 8位单片机&#xff1a; 8位单片机通常适用于相对简单的应用&#xff0c;如传感器控制、LED显示、小…

代码随想录算法训练营第三十五天| 860.柠檬水找零 406.根据身高重建队列 452. 用最少数量的箭引爆气球

860.柠檬水找零 本题看上好像挺难&#xff0c;其实挺简单的&#xff0c;大家先尝试自己做一做。 代码随想录 public boolean lemonadeChange(int[] bills) {int five 0;int ten 0;for (int i 0; i < bills.length; i) {if (bills[i] 5) {five;} else if (bills[i] 10)…

Mysql同步数据到Doris的踩坑过程

问题背景 由于项目需要&#xff0c;需要把多个Mysql数据库的数据同步到Doris数据库&#xff0c;然后利用Doris强调的计算和查询能力&#xff0c;来满足业务需求。有关Doris可以查看它的官方文档来了解它。 seatunnel的使用到放弃 缘起 从《第十届GIAC全球互联网架构大会》了…

论如何在Android中还原设计稿中的阴影

每当设计稿上注明需要添加阴影时&#xff0c;Android上总是显得比较棘手&#xff0c;因为Android的阴影实现方式与Web和iOS有所区别。 一般来说阴影通常格式是有&#xff1a; X: 在X轴的偏移度 Y: 在Y轴偏移度 Blur: 阴影的模糊半径 Color: 阴影的颜色 何为阴影 但是在A…

手刻 Deep Learning -第壹章 -PyTorch教学-激励函数与感知机入门(上)

一、前言 本文接续前篇教学 Pytorch 与线性回归 &#xff0c;本文着重在 Activation Function &#xff08; 中文称 激励函数 &#xff09;&#xff0c;我们会介绍激励函数 &#xff08;也有人称 激活函数&#xff1f; 激发函数&#xff1f; &#xff09; 为什么会有用&#xf…

Python 06 之面向对象基础

&#x1f600;前言 在日常编程和软件开发中&#xff0c;我们通常会遇到各种各样的问题&#xff0c;其中很多问题都可以通过面向对象的程序设计方法来解决。面向对象编程不仅可以使代码更加组织化和系统化&#xff0c;而且还可以提高代码的重用性和可维护性。 . 在本教程中&…

一个工位的演讲

前几天&#xff0c;知乎官方制作了一个视频&#xff0c;感觉这个视频非常不错&#xff0c;推荐给大家看看。 这个短片很有创意&#xff0c;而且&#xff0c;内容也很丰富。 为什么说这个短片很有创意呢&#xff1f;因为它是从一个工位的角度切入的&#xff0c;所谓铁打的工位&a…

基于matlab实现的电力系统稳定性分析摆幅曲线代码

完整程序&#xff1a; clear; clc; t 0; tf 0; tfl 0.5; tc 0.5; % tc 0.05, 0.125, 0.5 sec for 2.5 cycles, 6.25 cycles & 25 cycles resp ts 0.05; m 2.52 / (180 * 50); i 2; dt 21.64 * pi / 180; ddt 0; time(1) 0; ang(1) 21.64; pm 0.9; pm1 2.44;…