LeetCode 热题100——单调栈

news2024/12/23 0:51:49

  个人主页:日刷百题

系列专栏〖C语言小游戏〗〖Linux〗〖数据结构〗 〖C语言〗

🌎欢迎各位点赞👍+收藏⭐️+留言📝 

 写在前面:

递增单调栈:栈中元素从栈底到栈顶依次增大
递减单调栈:栈中元素从栈底到栈顶依次减小

在学习完朴素的数据结构栈之后,单调栈作为栈的一种升级版应用,在某些情境下具有得天独厚的优越性:可将O(n²)的遍历复杂度降低至O(n)

以下就是几种经典的单调栈运用问题。

一、字符串解码 

 

 思路:这题利用了栈,但不是单调栈,我们看到括号问题容易联想到有效括号问题也是利用栈

(1)遍历字符数组,当没有遇到‘]’时,将字符全部入栈

(2)若遇到‘]’,将字母一一出栈,入到临时栈,直到遇到‘[’停止

(3)此时将'['出栈,此时栈顶必然是数字字符,将数字字符全部转化为mulsize数字,出栈

(4)用2层嵌套循环,外层为mulsize,内层为临时栈的元素个数,将临时栈元素按mulszie次循环放进栈中,最后将临时栈初始化

(5)最后,字符遍历结束,栈中元素即为所求,此时将栈的末尾加上’\0’.

注:这里值得注意的地方有俩点

<1>在栈末尾插入‘\0’,得有扩容判断

<2>在将数字字符转化为mulsize时,字符数字是一个个出栈,为逆序,例如:100出栈为001,所以转化为数字的时候,要注意

typedef char DateType;
typedef struct Stack
{
    DateType* a;
    int top;
    int capacity;
}Stack;
//初始化和销毁栈
void InitStack(Stack* ps)
{
    assert(ps);
    ps->a = NULL;
    ps->top = ps->capacity = 0;
}
void DestoryStack(Stack* ps)
{
    assert(ps);
    free(ps->a);
    ps->a = NULL;
    ps->top = 0;
    ps->capacity = 0;
}

//出栈和入栈
void StackPush(Stack* ps, DateType x)
{
    assert(ps);
    if (ps->top == ps->capacity)
    {
        int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
        DateType* tmp = (DateType*)realloc(ps->a, sizeof(DateType) * newcapacity);
        if (tmp == NULL)
        {
            perror("realloc fail:");
            return;
        }
        ps->a = tmp;
        ps->capacity = newcapacity;
    }
    ps->a[ps->top] = x;
    ps->top++;
}
void StackPop(Stack* ps)
{
    assert(ps);
    assert(ps->top > 0);
    ps->top--;
}

//栈的有效个数和栈顶元素
int StackSize(Stack* ps)
{
    assert(ps);
    return ps->top;
}
DateType StackTop(Stack* ps)
{
    assert(ps);
    assert(ps->top > 0);
    return   ps->a[ps->top - 1];
}
//判空
bool IsEmptyStack(Stack* ps)
{
    assert(ps);
    return ps->top == 0;
}
char* decodeString(char* s) {
    Stack ps;
    Stack tmp;
    InitStack(&ps);
    InitStack(&tmp);
    int len = strlen(s);
    while ((*s) != '\0')
    {
        if ((*s) != ']')
        {
            StackPush(&ps, (*s));
        }
        else
        {
         
            while (StackTop(&ps) != '[')
            {
                char b = StackTop(&ps);
                StackPop(&ps);
                StackPush(&tmp, b);
              
            }
            StackPop(&ps);
            int tmpsize = tmp.top;
            int mulsize=0;
            int i=0;
            while (ps.top > 0 && ps.a[ps.top - 1] >= '0' && ps.a[ps.top - 1] <= '9')
            {


                mulsize =mulsize  + pow(10,i)*(ps.a[ps.top - 1] - '0');
                StackPop(&ps);
                i++;
            }
            for (int i = 0; i < mulsize; i++)
            {
                for (int j = 0; j < tmpsize; j++)
                {
                    char w = StackTop(&tmp);
                    StackPush(&ps, w);
                    StackPop(&tmp);
                }
                tmp.top = tmpsize;
            }

        }
        if (tmp.a != NULL)
        {
            free(tmp.a);
            tmp.a = NULL;
            InitStack(&tmp);
        }
        s++;
    }
    DestoryStack(&tmp);
    if (ps.top == ps.capacity)
    {
        int newcapacity = ps.capacity == 0 ? 4 : 2 * ps.capacity;
        DateType* tmp = (DateType*)realloc(ps.a, sizeof(DateType) * newcapacity);
        if (tmp == NULL)
        {
            perror("realloc fail:");
            return 1;
        }
        ps.a = tmp;
        ps.capacity = newcapacity;
    }
    ps.a[ps.top] = '\0';
    return ps.a;
}

 二、接雨水

思路:这题思路比较巧妙,运用了单调递减栈

(1)创造栈用来存放数组元素下标

(2)遍历数组,若栈为空或者栈顶元素所对应的数组值大于等于数组元素,则直接入栈

(3)若栈顶元素所对应数组元素值小于数组元素,则做出判断,将栈顶元素保存,且出栈,再用此时的栈顶元素所对应的数组值与数组元素比较,俩个数的较小值减去原来保存的栈顶元素所对应数组值即为接雨水凹槽的高,此时数组下标与栈顶差值减1即为接雨水凹槽的宽,相乘即为所接雨水的面积,保持循环,直到栈为空或者栈为递减,退出循环,进行数组下一个元素比较。

上面思路听起来比较复杂,可以看图理解:

typedef int DateType;
typedef struct  Stack
{
    DateType* a;
    int top;
    int capacity;
}Stack;
//初始化和销毁
void InitStack(Stack* ps)
{
    assert(ps);
    ps->a = NULL;
    ps->top = ps->capacity = 0;
}
void DestroyStack(Stack* ps)
{
    assert(ps);
    free(ps->a);
    ps->a = NULL;
    ps->top = ps->capacity = 0;
}
//出栈和入栈
void StackPush(Stack* ps, DateType x)
{
    assert(ps);
    if(ps->top == ps->capacity)
    {
        int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 4;
        DateType* tmp = (DateType*)realloc(ps->a,sizeof(DateType) * newcapacity);
        if (tmp == NULL)
        {
            perror("realloc fail:");
            return;
        }
        ps->capacity = newcapacity;
        ps->a = tmp;
    }
    ps->a[ps->top] = x;
    ps->top++;

}
void StackPop(Stack* ps)
{
    assert(ps);
    assert(ps->top > 0);
    ps->top--;
}
//栈顶元素和元素个数
int StackSize(Stack* ps)
{
    assert(ps);
    return  ps->top;
}
DateType StackTop(Stack* ps)
{
    assert(ps);
    assert(ps->top > 0);
    return ps->a[ps->top - 1];
}

//判空
bool  IsEmptyStack(Stack* ps)
{
    assert(ps);
    return ps->top == 0;
}
int MIN(int x, int y)
{
    return x > y ? y : x;
}
int trap(int* height, int heightSize) {
    Stack ps;
    InitStack(&ps);
    int count = 0;
    for (int i = 0; i < heightSize; i++)
    {
        while (ps.top > 0 && height[StackTop(&ps)] < height[i])
        {
            DateType tmp = StackTop(&ps);
            StackPop(&ps);
            if (ps.top == 0)
            {
                break;
            }
            int h = MIN(height[StackTop(&ps)], height[i]);
            int width = i - StackTop(&ps) - 1;
            count += (h - height[tmp]) * width;
        }

        StackPush(&ps, i);


    }
    DestroyStack(&ps);
    return count;
}

三、每日温度 

 

 思路:这题思路比较巧妙,运用了单调递减栈和上面一题类似

(1)创造栈用来存放数组元素下标

(2)遍历数组,若栈为空或者栈顶元素所对应的数组值大于等于数组元素,则直接入栈

(3)若栈顶元素所对应数组元素值小于数组元素,则做出判断,将栈顶元素保存,且出栈,由于当前数组元素大于栈顶元素对应数组元素值,而且一定是第一个大于栈顶元素对应数组元素值,直接求出下标差(当前数组元素下标和栈顶元素差)就是二者的距离,放入所求目标数组内(数组下标为保存的栈顶元素)。继续看新的栈顶元素,循环往复,直到当前数组元素小于等于栈顶元素所对应数组值或者栈为空停止,然后将数组元素下标入栈,进行数组下一个元素比较

(3)数组遍历结束后,栈为单调递减栈,里面元素所对应数组值(气温)向后检索找不到比它高的温度,所以以这些栈元素为下标的目标数组元素全部置为0.

图解如下:

typedef int DateType;
typedef struct  Stack
{
    DateType* a;
    int top;
    int capacity;
}Stack;
//初始化和销毁
void InitStack(Stack* ps)
{
    assert(ps);
    ps->a = NULL;
    ps->top = ps->capacity = 0;
}
void DestroyStack(Stack* ps)
{
    assert(ps);
    free(ps->a);
    ps->a = NULL;
    ps->top = ps->capacity = 0;
}
//出栈和入栈
void StackPush(Stack* ps, DateType x)
{
    assert(ps);
    if(ps->top == ps->capacity)
    {
        int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 4;
        DateType* tmp = (DateType*)realloc(ps->a,sizeof(DateType) * newcapacity);
        if (tmp == NULL)
        {
            perror("realloc fail:");
            return;
        }
        ps->capacity = newcapacity;
        ps->a = tmp;
    }
    ps->a[ps->top] = x;
    ps->top++;

}
void StackPop(Stack* ps)
{
    assert(ps);
    assert(ps->top > 0);
    ps->top--;
}
//栈顶元素和元素个数
int StackSize(Stack* ps)
{
    assert(ps);
    return  ps->top;
}
DateType StackTop(Stack* ps)
{
    assert(ps);
    assert(ps->top > 0);
    return ps->a[ps->top - 1];
}

//判空
bool  IsEmptyStack(Stack* ps)
{
    assert(ps);
    return ps->top == 0;
}

int* dailyTemperatures(int* temperatures, int temperaturesSize, int* returnSize) {
    int *answer=(int *)malloc(sizeof(int)*temperaturesSize);
    Stack  st;
    InitStack(&st);
    for(int i=0;i<temperaturesSize;i++)
    {
        while(st.top>0&&temperatures[i]>temperatures[StackTop(&st)])
        {
                answer[StackTop(&st)]=i-StackTop(&st);
                StackPop(&st);
                if(st.top==0)
                {
                    break;
                }
                
        }
       
            StackPush(&st,i);
        
    }
    while(st.top>0)
    {
        answer[StackTop(&st)]=0;
        StackPop(&st);
    }
     * returnSize=temperaturesSize;
    return answer;
    
}

四、柱状图中最大的矩形 

 

思路:这题思路与接雨水问题一样,不过此题用的是严格单调增栈 

(1)创造栈用来存放数组元素下标

(2)遍历数组,若栈为空或者栈顶元素所对应的柱形高度小于当前柱形高度,则当前柱形高度的数组下标直接入栈

(3)若栈顶元素所对应数组元素值大于等于数组元素,则做出判断,将栈顶元素临时保存,且出栈,再用当前数组元素下标与栈顶元素做差-1即为临时保存的栈顶元素所对应柱形高度的宽,根据原来临时保存栈顶元素求出其对应的高,就可以求出该高度的最大矩形面积,,保持循环,直到栈为空或者栈为严格递增,退出循环,进行数组下一个元素比较。

(4)数组遍历结束,栈为严格单调递增栈,除了最后一个栈底元素外,其他栈元素对应柱形高度最大矩形宽度为数组长度减去当前栈元素左侧一个栈元素的值-1,栈底元素对应柱形高度最大矩形宽度为数组元素长度

注:这里面有几个注意的细节

<1>当栈元素为1个,且数组元素小于等于栈顶对应柱形高度,此时临时保存栈顶元素,出栈,此临时保存栈顶元素对应柱形高度所能扩展做大矩形宽度为:当前数组元素下标i减去临时保存的栈顶元素

<2>数组元素等于栈顶栈顶对应柱形高度时,虽然所求的最大矩形不是这个栈顶的最大矩形,但是要小于这个栈顶元素对应的最大矩形面积,不碍事,直到下一个数组元素严格小于栈顶元素对应柱形高度,此时所求的最大矩形面积即之前那个相等高度的最大矩形面积,所以不影响

图解如下:

typedef int DateType;
typedef struct  Stack
{
    DateType* a;
    int top;
    int capacity;
}Stack;
//初始化和销毁
void InitStack(Stack* ps)
{
    assert(ps);
    ps->a = NULL;
    ps->top = ps->capacity = 0;
}
void DestroyStack(Stack* ps)
{
    assert(ps);
    free(ps->a);
    ps->a = NULL;
    ps->top = ps->capacity = 0;
}
//出栈和入栈
void StackPush(Stack* ps, DateType x)
{
    assert(ps);
    if(ps->top == ps->capacity)
    {
        int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 4;
        DateType* tmp = (DateType*)realloc(ps->a,sizeof(DateType) * newcapacity);
        if (tmp == NULL)
        {
            perror("realloc fail:");
            return;
        }
        ps->capacity = newcapacity;
        ps->a = tmp;
    }
    ps->a[ps->top] = x;
    ps->top++;

}
void StackPop(Stack* ps)
{
    assert(ps);
    assert(ps->top > 0);
    ps->top--;
}
//栈顶元素和元素个数
int StackSize(Stack* ps)
{
    assert(ps);
    return  ps->top;
}
DateType StackTop(Stack* ps)
{
    assert(ps);
    assert(ps->top > 0);
    return ps->a[ps->top - 1];
}

//判空
bool  IsEmptyStack(Stack* ps)
{
    assert(ps);
    return ps->top == 0;
}
int  MAX(int x, int y)
{
    return x>y?x:y;
}

int largestRectangleArea(int* heights, int heightsSize) {
    Stack st;
   InitStack(&st);
    int max = 0;
   
    for (int i = 0; i < heightsSize; i++)
    {
        while (st.top>0 && heights[i] <= heights[StackTop(&st)])
        {
            int tmp = StackTop(&st);
            StackPop(&st);
            if(st.top==0)
            {
                max=MAX(max, heights[tmp]*i);
                break;
            }
            int width = i - StackTop(&st) - 1;
            max = MAX(max, heights[tmp] * width);
        }

          StackPush(&st, i);//严格单调增
        
    }
    //遍历结束后,变为严格单调递增栈
    while (st.top>0)
    {
        int tmp =StackTop(&st);
        StackPop(&st);
        if (st.top==0)
        {
            max = MAX(max, heights[tmp] * heightsSize);
          break;
        }
        int width = heightsSize - StackTop(&st)-1 ;
        max = MAX(max, heights[tmp] * width);
       
    }
    return max;
   

}

 总结:本篇文章讲解了单调栈的应用,为系列题目,有利于帮助理解单调栈的用法和这系列问题思路。

希望大家阅读完可以有所收获,同时也感谢各位铁汁们的支持。文章有任何问题可以在评论区留言,百题一定会认真阅读!

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

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

相关文章

SE-Net:Squeeze-and-Excitation Networks(CVPR2018)

文章目录 AbstractIntroduction表征的重要性以前的方向本文提出 Related WorkDeeper ArchitectureAlgorithmic Architecture SearchAttention and gating mechanisms Squeeze-and-Excitation BlocksSqueeze: Global Information EmbeddingExcitation: Adaptive RecalibrationIn…

Temporary failure in name resolution

报错&#xff1a; 1.打开resolv.conf文件 sudo vim /etc/resolv.conf 2. 确保resolv.conf文件至少包含一个名称服务器。列出名称服务器的行应如下所示&#xff1a; 3. 保存文件并退出。 4. 接下来&#xff0c;重新启动DNS 解析器服务。运行以下命令&#xff1a; sudo syste…

androidStudio版本下载链接记录

androidStudio 最新官网版本&#xff1a; 下载 Android Studio 和应用工具 - Android 开发者 | Android DevelopersAndroid Studio 提供了一些应用构建器以及一个已针对 Android 应用进行优化的集成式开发环境 (IDE)。立即下载 Android Studio。https://developer.android.g…

创建型模式 | 原型模式

一、原型模式 1、原理 原型模式&#xff0c;用原型实例指定创建对象的种类&#xff0c;并且通过拷贝这些原型创建新的对象。原型模式其实就是从一个对象再创建另外一个可定制的对象&#xff0c;而且不需要知道任何创建的细节。原型像是一个模板&#xff0c;可以基于它复制好多…

二叉树题目:输出二叉树

文章目录 题目标题和出处难度题目描述要求示例数据范围 前言解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;输出二叉树 出处&#xff1a;655. 输出二叉树 难度 6 级 题目描述 要求 给定二叉树的根结点 root \textt…

探索未来交通!空客、宝马开启新一轮“量子计算挑战赛”

12月6日&#xff0c;空中客车公司和宝马集团共同发起了一项名为 “量子交通探索”的全球量子计算挑战赛&#xff0c;以应对航空和汽车领域最紧迫的挑战——这些挑战对于传统计算机而言仍然是难以克服的。 这项挑战是首创性的&#xff0c;它将两个全球行业领导者聚集在一起&…

堆的时间复杂度

1、堆排序的时间复杂度为O(nlogn) 2、对N个元素建堆的时间复杂度为O(N)&#xff0c;删除堆顶元素的时间复杂度为O(logN)&#xff0c;因此删除堆所有元素的时间复杂度为O(NlogN)。 3、不管数组初始时是有序的还是逆序的&#xff0c;堆排序都会先建堆&#xff0c;变成了堆序的性…

《每天一分钟学习C语言·五》

1、 给一个字符数组输入字符串 char arr[10]; gets[arr]; //gets函数接收回车符&#xff0c;如果直接按回车&#xff0c;gets函数会把回车符转变成空字符作为结束&#xff0c;即arr[0]’\0’;2、 文件结尾标志ctrlz表示返回NULL 自己定义的头文件里面一般有宏定义和声明&#…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Button按钮组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Button按钮组件 一、操作环境 操作系统: Windows 10 专业版 IDE:DevEco Studio 3.1 SDK:HarmonyOS 3.1 二、Button按钮组件 Button 组件也是基础组件之一&#xff0c;和其它基础组件不…

QT打包exe文件,在其它电脑里双击exe就可以直接运行

想要不依赖QT环境&#xff0c;在其它电脑里直接双击exe文件就可以运行当前程序。具体打包过程如下&#xff1a; 使用QT编译出release版本的exe release版本运行无误后&#xff0c;需要找到当前构建生成的exe所在文件夹 可以看到具体目录在这里 我在该目录下的bin文件夹里找到…

ICC2:Less than minimum edge length和Concave convex edge enclosure

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 首先,要介绍一下这两种drc Less than minimum edge length对应的tf rule如下: 而Concave convex edge enclosure对应图示和tf 规则如下,可

linux中vim命令修改jar包中的文件内容

文章目录 概述vim命令修改配置文件 概述 首先问问为什么要直接修改jar包中的文件&#xff0c;而不是重新打包&#xff0c;在非必要的情况下&#xff0c;不要直接修改jar包&#xff0c;这样容易出事故&#xff1b; 当然也有一些场景不得不修改jar包&#xff0c;比如&#xff1a…

TrustZone之示例用例——空中固件更新

这第二个示例涉及更新引导固件。我们系统的要求如下&#xff1a; • 新的固件镜像通过网络提供。 • 只能安装经过身份验证的镜像。 • 固件版本不能回滚。 为了实现这些目标&#xff0c;OEM 使用其私钥对图像进行签名。下载设备配备了公钥&#xff0c;它可以用来验证签名。…

3D模型人物换装系统(三 优化合批处理,提取合批配置可,封装)

3D模型人物换装系统三&#xff08;优化合批处理&#xff0c;提取合批配置可&#xff0c;封装&#xff09; 介绍法线贴图问题规划以及封装缺陷修改整理 整合总结 介绍 本文使用2018.4.4和2020.3.26进行的测试 这里先说一下我上一篇没有解决的问题&#xff0c;法线贴图不正确&am…

24岁,拿到18K,我真的很卷?

前言 前段时间去面试了一个公司&#xff0c;成功拿到了offer&#xff0c;薪资也从12k涨到了18k&#xff0c;对于工作还没两年的我来说&#xff0c;还是比较满意的&#xff0c;毕竟一些工作3、4年的可能还没我高。 我可能就是大家说的卷王&#xff0c;感觉自己年轻&#xff0c…

MySQL,使用Union组合查询

1、基本使用 Union可将多条select语句组合成一个结果集&#xff0c;常见的使用场景有2种&#xff1a; 在单个查询中&#xff0c;从不同的表返回类似结构的数据&#xff1b;对单个表执行多个查询&#xff0c;按单个查询返回数据。 例&#xff1a;检索出所有价格<50的产品&…

java多线程创建的三种方式

第一种 第二种 第三种&#xff08;想获得线程的执行结果&#xff0c;建议使用这种&#xff09;

2023 英特尔On技术创新大会直播 |探索视觉AI的无限可能

2023 英特尔On技术创新大会直播 | 探索视觉AI的无限可能 前言一未来的 AI&#xff1a;释放视觉 AI 真正潜力二AI技术突破、视觉Al挑战及前沿研究创新三全尺度视觉学习全尺度视觉学习示例1.GridConv 实现三维人体姿态估计更高准确率2.KW 预训练及迁移模型性能3.无数据增强稠密对…

8.基于Cortex-M4内核的STM32F40x中断分析

通用中断知识铺垫1&#xff1a; 完整的CM4有256个可编程中断&#xff08;16个内核中断和240个外部中断&#xff09;&#xff0c;而stm32f40x共有92个中断&#xff08;10内82可编程&#xff09;&#xff0c;意思是说STM32F40X这个单片机没有完全释放CM4内核的资源。 CM4内核的中…

赴日IT培训课程 程序员新思路!

先说好&#xff0c;跟国内相比&#xff0c;日本IT并不发达。日本IT是依托着日本传统强势的制造业和政府机关发展的&#xff0c;所以开发的大多数软件也是面向这些的&#xff0c;由于日本人的严谨态度&#xff0c;各种文档的编写层出不穷&#xff0c;不像国内程序员每天没日没夜…