代码随想录算法训练营第58天 | 单调栈 ●739 每日温度 ●496下一个更大元素I ●503下一个更大元素II ●42 接雨水 ●84 柱形图中最大的矩形

news2024/12/24 3:12:46

#单调栈:

单调栈就是保持栈内元素有序。和栈与队列(239. 滑动窗口最大值 自己写一个class来实现单调队列)一样,需要我们自己维持顺序,没有现成的容器可以用。

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

单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素,优点是整个数组只需要遍历一次。

#739 每日温度 

用双指针半天写不出。gpt说这种题不能用双指针,得用单调栈。然后用单调栈自己写的:

stack里存的index,遇到更大的就记录,pop原来的,push新的。但是注意,会有不断遇到pop完之后新的top还是比新来的T[i]小,这时候要不断pop,全部pop完之后再 push一次。而不是push多次

vector<int> dailyTemperatures(vector<int>& T) {
       vector<int> res(T.size(),0);
       stack<int> mys;//store the index
       mys.push(0);
       for(int i=1;i<T.size();i++){
           if(T[i]<=T[mys.top()]) mys.push(i);
           else{
                while(!mys.empty() && T[i]>T[mys.top()]){
                    res[mys.top()]=i-mys.top();
                    mys.pop();
                }
                mys.push(i);
            }
       }
       return res;
    }

# 496下一个更大元素I

居然是easy,我不觉得是easy,感觉难度>=#739 每日温度 。太好了,随想录也 “ 看上去和739几乎一样,但是这么绕了一下,其实还上升了一点难度。需要对单调栈使用的更熟练一些,才能顺利的把本题写出来。”

自己写的时候只能想到先像#739那样,把一个弄一个一一对应的vector res把下一个最大的答案存进去,然后再从nums1去nums2里一一对应着找。我自己做的时候最困惑我的一点是:nums1和nums2顺序不同,nums1就很难用

下面是我的AC代码,基于#739的,然后加了一段 O n^2,感觉很慢

vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        vector<int> res(nums2.size(),-1);
        vector<int> res1(nums1.size(),-1);
        stack<int> mys;//store the index
        mys.push(0);
        for(int i=1;i<nums2.size();i++){
            if(nums2[i]<=nums2[mys.top()]) mys.push(i);
            else{
                    while(!mys.empty() && nums2[i]>nums2[mys.top()]){
                        res[mys.top()]=nums2[i];
                        mys.pop();
                    }
                    mys.push(i);
                }
        }
        for(int i=0;i<nums1.size();i++){
            for(int j=0;j<nums2.size();j++){
                if(nums1[i]==nums2[j]) res1[i]=res[j];
            }
        }
        return res1; 
    }

随想录思路:

对于nums1和nums2找对应: “遍历nums2过程中,要判断nums2[i]是否在nums1(小)中出现过,因为最后是要根据nums1元素的下标来更新result数组。注意题目中说是两个没有重复元素 的数组 nums1 和 nums2。没有重复元素,就可以用map来做映射了。根据数值快速找到下标,还可以判断nums2[i]是否在nums1中出现过。

C++中,当我们要使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的。”

改成用unordered map之后快太多了。然后记得if(umap.count(nums2[mys.top()])>0)这个不要忘了,不然不存在的value从umap里获取的idx是0,会出错

vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        vector<int> res(nums1.size(),-1);
        unordered_map<int,int> umap;
        for(int i=0;i<nums1.size();i++){
            umap[nums1[i]]=i;//key:val, value:idx
        }
    
        stack<int> mys;//store the index
        mys.push(0);
        for(int i=1;i<nums2.size();i++){
            if(nums2[i]<=nums2[mys.top()]) mys.push(i);
            else{
                    while(!mys.empty() && nums2[i]>nums2[mys.top()]){
                        if(umap.count(nums2[mys.top()])>0){
                            int idx_nums1=umap[nums2[mys.top()]];
                            res[idx_nums1]=nums2[i];
                        }
                        mys.pop();
                    }
                    mys.push(i);
                }
        }
        return res; 
    }

# 503下一个更大元素II 变体:circular

自己写的 20min:基础款思路+for(int i=1;i<2*nums.size()-1;i++) for loop多走一次,帮最后一个元素再从头走完; 这种题写的时候要分清是idx还是value,比如我会忘记应该是nums[st.top()]<nums[idx] 而不是直接st.top()<nums[idx]. 随想录和我思路一样。

vector<int> nextGreaterElements(vector<int>& nums) {
        stack<int> st;
        vector<int> res(nums.size(),-1);
        st.push(0);
        for(int i=1;i<2*nums.size()-1;i++){
            int idx=i<nums.size()?i:i-nums.size();
            //int idx=i%nums.size(); also ok
            if(nums[st.top()]>nums[idx]) st.push(idx);
            else{
                while(!st.empty() && nums[st.top()]<nums[idx]){
                    res[st.top()]=nums[idx];
                    st.pop();
                }
                st.push(idx);
            }
        }
        return res;
    }

#42 接雨水 Hard,可多种方法解,面试常考

方法有:双指针法即dp,单调栈法

第一个自己写出的hard!虽然我的ac code跑出来很慢:先求出和前面几个题一样的,右边第一个比自己大的,左边第一个比自己大的,两个vec

然后进一步处理,变成 right里面装 右边最大的(而不是右边第一个最大的),left里面装 从右往左,左边最大的(而不是第一个最大的)

然后left[i]和right[i]取小的那个,做为水的高度,vec[i]就是底的高度。

 int trap(vector<int>& vec) {
        
        vector<int> left(vec.size(),-1);
        vector<int> right(vec.size(),-1);
        stack<int> st;

        //find first right larger
        st.push(0);
        for(int i=1;i<vec.size();i++){
            if(vec[i]<=vec[st.top()]) st.push(i);
            else{
                while(!st.empty() &&  vec[st.top()]<vec[i]){
                    right[st.top()]=vec[i];
                    st.pop();
                }
                st.push(i);
            }
        }

        //get right max
        
        int max=right[right.size()-1];
        for(int i=right.size()-2;i>=0;i--){
            if(right[i]!=-1) {
                max=std::max(max,right[i]);
                right[i]=max;
                //right[i]=std::max(right[i+1],right[i]);

            }
        }
        
        //find first left larger
        while(!st.empty()) st.pop();
        st.push(vec.size()-1);
        for(int i=vec.size()-2;i>=0;i--){
            if(vec[i]<=vec[st.top()]) st.push(i);
            else{
                while(!st.empty() &&  vec[st.top()]<vec[i]){
                    left[st.top()]=vec[i];
                    st.pop();
                }
                st.push(i);
            }
        }
        
        //get left max
        max=left[0];
        for(int i=1;i<left.size();i++){
            if(left[i]!=-1){
                left[i]=std::max(left[i-1],left[i]);
            } 
        }
        
        //cal res
        int sum=0;
        for(int i=0;i<vec.size();i++){
            if(left[i]!=-1 && right[i]!=-1){
                sum+=min(left[i],right[i])-vec[i];
            }
        }
        return sum;
    }

但是!我这个思路太搞笑了,太复杂了,单调栈没用到点上

这样跑起来快多了,思路也正常了。根本不用stack 。这也就是双指针法(这个也是dp方法)

int trap(vector<int>& vec) {
        
        vector<int> left(vec.size(),-1);
        vector<int> right(vec.size(),-1);

        int max_right = 0;
        for(int i = vec.size() - 1; i >= 0; i--) {
            max_right = max(max_right, vec[i]);
            right[i] = max_right;
        }

        int max_left = 0;
        for(int i = 0; i < vec.size(); i++) {
            max_left = max(max_left, vec[i]);
            left[i] = max_left;
        }
        //cal res
        int sum=0;
        for(int i=0;i<vec.size();i++){
            if(left[i]!=-1 && right[i]!=-1){
                sum+=min(left[i],right[i])-vec[i];
            }
        }
        return sum;
    }

单调栈方法:

 栈顶和栈顶的下一个元素以及要入栈的元素,三个元素来接水!

 

int trap(vector<int>& height) {
        stack<int> st; 
        st.push(0);
        int sum = 0;
        for (int i = 1; i < height.size(); i++) {
            if (height[i] < height[st.top()]) {     
                st.push(i);
            } if (height[i] == height[st.top()]) { 
                //updatt, so we can use the right most pole
                st.pop(); 
                st.push(i);
            } else {                                
                while (!st.empty() && height[i] > height[st.top()]) { 
                    int mid = st.top();//stack top
                    st.pop();
                    if (!st.empty()) {//stack top next
                        int h = min(height[st.top()], height[i])- height[mid];
                        int w = i - st.top() - 1; 
                        //printf("h: %d, w: %d from %d -%d\n",h,w,i,st.top());
                        sum += h*w;
                    }
                }
                st.push(i);
            }
        }
        return sum;
    }

# 84 柱形图中最大的矩形

想了半天,好难,不会做。本来想用dp做,递推公式想不出来。但是请记住,递推公式发现太麻烦复杂想不出来,也是一种进展!说明我们发现用dp表不合适,要换一种方法了!

 不懂为什么随想录说是为了防止死循环。

大概思路是图里这样,对每个柱子,相当于找高度和他高度一样的矩形

 

 while (t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t];
            minLeftIndex[i] = t;

这里我觉得好难理解。其实就是现在要找i左边第一个小于他的,左边第一个是t,不满足。接下来我们要找的还是要比i小的,那至少也要比t小,既然t的左边第一个小于的已经找到了,那我们就用他的。但也要重新和t代表的值比一下大小。

 int largestRectangleArea(vector<int>& heights) {
        vector<int> minLeftIndex(heights.size());
        vector<int> minRightIndex(heights.size());
        int size = heights.size();

        // 每柱子 左第一个小于该柱子的下标
        minLeftIndex[0] = -1; // 注意这里初始化,防止下面while死循环
        for (int i = 1; i < size; i++) {
            int t = i - 1;
            while (t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t];
            minLeftIndex[i] = t;
        }
        // 每柱子 右第一个小于该柱子的下标
        minRightIndex[size - 1] = size; // 注意这里初始化,防止下面while死循环
        for (int i = size - 2; i >= 0; i--) {
            int t = i + 1;
            
            while (t < size && heights[t] >= heights[i]) t = minRightIndex[t];
            minRightIndex[i] = t;
        }
    
        int result = 0;
        for (int i = 0; i < size; i++) {
            int sum = heights[i] * (minRightIndex[i] - minLeftIndex[i] - 1);
            result = max(sum, result);
        }
        return result;
    }

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

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

相关文章

浅谈物联网工程专业:技术融合与未来发展

技术融合与未来发展 引言1. 专业的定义与概述2. 专业的知识体系3. 专业的实践应用4. 专业的发展趋势5. 专业的就业前景结语&#x1f340;小结&#x1f340; &#x1f389;博客主页&#xff1a;小智_x0___0x_ &#x1f389;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收…

zabbix 企业级级监控(1) 监控自己

重点一 Zabbix简介在企业网络运维过程中&#xff0c;管理员必须随时关注各服务器和网络的运行状况&#xff0c;以便及时发现问题&#xff0c;尽可能减少故障的发生。当网络中的设备&#xff0c;服务器等数量较多时&#xff0c;为了更加方便&#xff0c;快捷的获得监控信息&…

【软件测试面试】腾讯数据平台笔试题-接口-自动化-数据库

数据库题 答案&#xff1a; Python编程题 答案&#xff1a; 接口参数化题 答案&#xff1a; 接口自动化题 答案&#xff1a; 以下是我收集到的比较好的学习教程资源&#xff0c;虽然不是什么很值钱的东西&#xff0c;如果你刚好需要&#xff0c;可以评论区&#…

6.3.6 利用Wireshark进行协议分析(六)----网页提取过程的协议分析

6.3.6 利用Wireshark进行协议分析&#xff08;六&#xff09;----网页提取过程的协议分析 利用Wireshark捕获网页访问过程中产生的应用协议报文&#xff0c;还原Web服务中报文的交互过程&#xff0c;为了防止网页直接从本地缓存中获取&#xff0c;我们首先需要清空浏览器保存的…

GO语言GMP模型

目录 程序入口 协程主动让出: 被动让出: schedule 监控线程 程序入口 在执行一系列检查和初始化&#xff08;创建多少个P&#xff0c;与M&#xff10;关联&#xff09;后&#xff0c;进入runtime.main,创建main goroutine,执行mian.mian。 一开始GO语言的调度只有M和G。每个M…

【代码随想录 | Leetcode | 第七天】链表 | 链表相交 | 环形链表 II

前言 欢迎来到小K的Leetcode|代码随想录|专题化专栏&#xff0c;今天将为大家带来链表相交和环形链表 II的分享✨ 目录 前言面试题 02.07. 链表相交142. 环形链表 II总结 面试题 02.07. 链表相交 ✨题目链接点这里 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找…

C/C++ new A与new A()的区别

在C中&#xff0c;POD是“Plain Old Data”的缩写&#xff0c;即“普通旧数据”。POD data是指一种特殊类型的数据结构&#xff0c;它们具有简单的内存布局&#xff0c;没有构造函数、虚函数、私有/保护非静态数据成员&#xff0c;也没有虚继承等特性。这些数据结构可以直接通过…

k8s与集群管理

从docker讲起 终于有人把 Docker 讲清楚了&#xff0c;万字详解&#xff01; Docker资源&#xff08;CPU/内存/磁盘IO/GPU&#xff09;限制与分配指南 默认情况下&#xff0c;Docker容器是没有资源限制的&#xff0c;它会尽可能地使用宿主机能够分配给它的资源。如果不对容器资…

C++--day3(内联函数、结构体、类、封装、this、构造函数、析构函数)

#include <iostream>using namespace std;class My_stack { private:int *ptr; //指向堆区空间int top; //记录栈顶元素int size; public://有参构造My_stack(int size):ptr(new int[size]),top(-1){this->sizesize;cout<<"My_stack::有参构造&…

基于STM32的智能喂养系统

基于STM32的智能喂养系统 系统简介 自动检测环境温湿度&#xff0c;当温湿度低于阈值时自动打开加湿器&#xff1b;自动检测水位&#xff0c;当水位低于阈值时自动加水&#xff1b;自动检测有害气体&#xff0c;当检测到有害气体时自动打开风扇&#xff1b;同步状态到微信小程…

中间件上云部署 zookeeper

中间件上云部署 zookeeper 企业级中间件上云部署 zookeeper一、环境说明二、zookeeper部署YAML资源清单准备三、zookeeper部署及部署验证四、zookeeper应用验证 企业级中间件上云部署 zookeeper 一、环境说明 storageclassingress 二、zookeeper部署YAML资源清单准备 # vim…

图解java.util.concurrent并发包源码系列,原子类、CAS、AtomicLong、AtomicStampedReference一套带走

图解java.util.concurrent并发包源码系列&#xff0c;原子类、CAS、AtomicLong、AtomicStampedReference一套带走 原子类为什么要使用原子类CAS AtomicLong源码解析AtomicLong的问题ABA问题AtomicStampedReference 高并发情况下大量的CAS失败&#xff0c;导致CPU空转 往期文章&…

百度智能汽车负责人储瑞松离职,智驾重心转向ANP3

作者 | 王博 HiEV从多个信息源获悉&#xff0c;百度集团副总裁、百度智能汽车事业部总经理储瑞松将从百度离职。一位知情人士透露&#xff0c;储瑞松「即将启程&#xff0c;返回美国」。 继百度Apollo技术骨干郭阳离职后&#xff0c;储瑞松的变动&#xff0c;更加直白地反映出百…

电动汽车高压测试方案

针对电动汽车道路试验的要求&#xff0c;风丘科技携手德国IPETRONIK共同推出了电动汽车高压测试方案。电动汽车测试通常有两种测量手段&#xff1a;第一种是测量模拟量信号&#xff0c;包括电压、电流、温度和高压&#xff1b;第二种是使用数据记录仪或CAN卡从车辆总线读取数据…

你一定要收好这个系统性能测试用例模板

引言 文档目的 [简述本文档的目的] 适用范围 [指明本文档的适用范围和读者对象。如本测试计划是在策略和方法的高度说明如何计划、组织和管理测试项目。测试计划应包含足够的信息&#xff0c;使测试人员明白项目需要做什么、是如何运作的。另外&#xff0c;测试计划只是测试的…

刚体运动学-速度和加速度的表示方法(连体坐标系和世界坐标系)

0. 符号定义 自己画了一个图 下标 b b b是连体坐标系原点 O b O_b Ob​相对世界坐标系原点 O p O_p Op​的矢量在世界坐标系下的表示。下标 p p p是观察点相对世界坐标系原点 O p O_p Op​的矢量在世界坐标系下的表示。下标 p / b p/b p/b是观察点相对连体坐标系原点 O b O_b…

Python实现将pdf,docx,xls,doc,wps,zip,xlsx,ofd链接下载并将文件保存到本地

前言 本文是该专栏的第31篇,后面会持续分享python的各种干货知识,值得关注。 在工作上,尤其是在处理爬虫项目中,会遇到这样的需求。访问某个网页或者在采集某个页面的时候,正文部分含有docx,或pdf,或xls,或doc,或wps,或ofd,或xlsx,或zip等链接。需要你使用python自…

Yalmip入门教程(2)-变量定义和操作

博客中所有内容均来源于自己学习过程中积累的经验以及对yalmip官方文档的翻译&#xff1a;https://yalmip.github.io/tutorials/ 1.决策变量的定义 1.1 sdpvar 上文简单介绍了sdpvar函数的用法&#xff0c;接下来将对其进行详细介绍。复习一下&#xff0c;sdpvar函数的基本语…

window10安装telnet

1、打开控制面板 2、点击程序和功能 3、点击启用或关闭Windows功能 4、选中Telnet客户端&#xff0c;然后点击确定&#xff0c;然后就可以使用telnent 主机 端口来查看本地是否能连通该主机的该端口。

Linux系统部署Tomcat详细教程(图文讲解)

前言&#xff1a;本篇博客教大家如何一步一步使用Linux系统去部署自己的Tomcat服务器&#xff0c;每一行代码都是我自己严格执行过的&#xff0c;共分为了8点进行阐述&#xff0c;逻辑清晰&#xff01; 目录 一、安装JDK环境 二、准备Tomcat安装包 三、安装Tomcat 四、配置…