Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >单调栈,好难。。。

单调栈,好难。。。

作者头像
五分钟学算法
发布于 2023-09-09 09:56:36
发布于 2023-09-09 09:56:36
24400
代码可运行
举报
文章被收录于专栏:五分钟学算法五分钟学算法
运行总次数:0
代码可运行

我给出了一个结论,基本上单调栈的代码 80% 都是类似下方的样子

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
for 从左向右
  while stack and stack[-1] > xx :
       逻辑操作
    
    stack.append(xx)

LeetCode 1475、商品折扣后的最终价格 一题中,我写的代码是从右向左遍历的。

LeetCode 739、每日温度 一题中,我写的的代码是从左向右遍历的。

这个时候,有个同学提出一个疑问:

实际上,单调栈的题目无论是从左向右还是从右向左遍历数组都是可以的,代码框架类似,我们以一道大厂真题来具体解释一下正序和逆序的一个区别。

题目描述

在学校中,N个小朋友站成一队, 第i个小朋友的身高为height[i],第i个小朋友可以看到的右边的第一个比自己身高更高的小朋友j,那么ji的好朋友(j > i)。请重新生成一个列表,对应位置的输出是每个小朋友的好朋友位置,如果没有看到好朋友,请在该位置用0代替。小朋友人数范围是 [0, 40000]

输入描述

第一行输入N,表示有N个小朋友

第二行输入N个小朋友的身高height[i],都是整数

输出描述

输出N个小朋友的好朋友的位置

示例一

输入
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
2
100 95
输出
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
0 0

示例二

输入
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
8
123 124 125 121 119 122 126 123
输出
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 2 6 5 5 6 0 0

解题思路

注意,本题和LC739. 每日温度非常类似。区别在于,本题需要找到的是右边下一个更大元素的索引,而非与当前元素的间隔,显然变得更加简单了。

我们讲过,类似这种要求寻找左边/右边最近的更大/更小元素的题目,均可以使用单调栈来完成。

对于单调栈的题目,既可以正序遍历也可以逆序遍历数组来完成,重点在于理解单调栈的原理,同学们只需要选择适合自己理解的方法来完成即可。以下表格总结了两种不同遍历顺序的异同点。

正序遍历

逆序遍历

单调栈顺序

栈中储存的索引所对应在原数组中的元素大小,从栈底至栈顶单调递减,即更大的数(的下标)位于栈底

入栈时机

栈顶元素反复出栈并修改ans之后,进行入栈。且入栈元素为当前下标i,而非身高h

修改ans时机

i为preIndex的下一个更大元素的下标,在出栈过程中,即在while内修改ans[preIndex]

stack[-1]为i的下一个更大元素的下标,在出栈结束后,即在while外修改ans[i]

出栈条件

h > height[stack[-1]]

h >= height[stack[-1]]

代码

解法一

正序遍历height构建单调栈。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 输入小朋友个数n
n = int(input())
# 输入N个小朋友的高度数组
height = list(map(int, input().split()))

# 构建一个单调栈,用来存放不同小朋友的身高的索引
# 栈中储存的索引所对应在height中的元素大小,从栈底至栈顶单调递减
# 即更大的数(的下标)位于栈底
stack = list()

# 构建列表ans,用来保存输出结果
# 初始化其中所有的元素均为0
ans = [0] * n

# 从头开始遍历每一个小朋友的身高
for i, h in enumerate(height):
    # 第i个小朋友的身高h,需要不断地与栈顶元素比较
    # 如果栈顶元素存在并且h【大于】栈顶元素stack[-1]
    # 意味着栈顶元素找到了右边最近的比他更高的身高h
    while len(stack) > 0 and h > height[stack[-1]]:
        # 首先获取栈顶元素的值,也就是上一个比h小的身高的索引值
        preIndex = stack.pop()

        # i即为preIndex这个索引所对应的,下一个最近身高
        ans[preIndex] = i

    # 再把当前小朋友身高的下标i存放到栈中
    # 注意:所储存的是下标i,而不是身高h
    stack.append(i)

# ans中的int元素转成str后才能合并成字符串
print(" ".join(map(str, ans)))

解法二

逆序遍历height构建单调栈。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 输入小朋友个数n
n = int(input())
# 输入N个小朋友的高度数组
height = list(map(int, input().split()))

# 构建一个单调栈,用来存放不同小朋友的身高的索引
# 栈中储存的索引所对应在height中的元素大小,从栈底至栈顶单调递增
# 即更大的数(的下标)位于栈底
stack = list()

# 构建列表ans,用来保存输出结果
# 初始化其中所有的元素均为0
ans = [0] * n

# 逆序遍历每一个小朋友的身高
for i in range(n-1, -1, -1):
    h = height[i]
    # 第i个小朋友的身高h,需要不断地与栈顶元素比较
    # 如果栈顶元素存在并且h【大于等于】栈顶元素stack[-1]
    # 说明栈顶元素stack[-1]并不是身高h右边最近的比h更大的元素
    # 需要将栈顶元素弹出,继续寻找比h大的栈顶元素
    while len(stack) > 0 and h >= height[stack[-1]]:
        # 栈顶元素下标对应的身高不大于当前身高h,不是符合要求的更大身高,弹出
        stack.pop()
    # 完成弹出后,如果栈顶仍存在元素,说明stack[-1]所对应的身高,是严格比h大的下一个身高
    if len(stack) > 0:
        # ans[i]修改为stack[-1]
        ans[i] = stack[-1]

    # 再把当前小朋友身高的下标i存放到栈中
    # 注意:所储存的是下标i,而不是身高h
    stack.append(i)

# ans中的int元素转成str后才能合并成字符串
print(" ".join(map(str, ans)))

时空复杂度

时间复杂度:O(N)。不管是正序还是逆序遍历,均仅需一次遍历height数组。

空间复杂度:O(N)。单调栈所占用的额外空间。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-08-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 五分钟学算法 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
单调栈,栈还能单调一下?
之前遇到一个算法题目,自己只会用时间复杂度 O(N^2) 暴力解法解决,有大佬说用单调栈,可以做到 O(N) 的时间复杂度,当时我的表情是这样的:
somenzz
2021/10/08
2K0
【甘泉算法】一文搞定单调栈问题
本文主要利用单调栈来解决leetcode上的典型问题,其实它的应用范围倒是不广,主要解决的都是类似于leetcode上下一个更大元素的问题,本文将从这类问题出发,帮助大家掌握单调栈的应用技巧。主要题型如下所示:
itlemon
2022/01/10
8620
【甘泉算法】一文搞定单调栈问题
单调栈
单调栈,就是一个栈,不过栈内元素保证单调性。即,栈内元素要么从小到大,要么从大到小。而单调栈维护的就是一个数前/后第一个大于/小于他的数。
为为为什么
2022/08/09
4610
单调栈
单调栈算法详解_单调栈和单调队列
https://leetcode-cn.com/problems/bao-han-minhan-shu-de-zhan-lcof/
全栈程序员站长
2022/11/08
2750
单调栈算法详解_单调栈和单调队列
六十五、下一个更大的数系列,单调栈解决方法
单调栈的实质是有单调性的栈,包括单调递增栈和单调递减栈。通过栈的入栈和出栈维护一个动态的滑动窗口,然后栈顶元素是该窗口的最大值或最小值,通过一次遍历,就可以计算出所有元素的下一个较大值或较小值。
润森
2022/08/17
4080
六十五、下一个更大的数系列,单调栈解决方法
单调栈(C/C++)
单调队列和单调栈都是一种数据结构,应用十分广泛,在蓝桥杯、ICPC、CCPC等著名编程赛事都是重点的算法,今天博主将自己对单调栈与单调队列的理解以及刷题的经验,用一篇博客分享给大家,希望对大家有所帮助,它们用于解决类似“寻找最大值与最小值”这样的问题。它们的区别在于如何维护数据的单调性。、
摆烂小白敲代码
2024/09/23
1200
单调栈(C/C++)
华为0906秋招笔试真题解析
给定某只股票连续N天的价格列表stockPrices,其中stockPrices[i]表示股票某天的价格,请生成一个新列表,对应位置输出为:要想等到股票价格上涨,至少需要等待的天数,如果股票价格不上涨,对应位置输出为0。
五分钟学算法
2023/09/09
5110
有趣的算法题~单调栈
在刷 LeetCode 的时候,每次遇到精彩的题解都会感叹数据结构的伟大,通过巧妙地设计,能够非常清晰明了的解决问题。
Wu_Candy
2022/07/04
3230
有趣的算法题~单调栈
单调栈简介
栈内元素非递增或者非递减。另一种说法是从栈底到栈顶递增或者递减。在很多情况下,可能会出现相同的数字元素,所以称之为非递增或者非递减栈比递增、递减栈更合适。
全栈程序员站长
2022/11/08
2280
用javascript分类刷leetcode13.单调栈(图文视频讲解)
我们怎样加速嵌套的这层循环呢,其实可以预先计算从左往右和从右往左的最大高度数组,在循环数组的时候,可以直接拿到该位置左右两边的最大高度,当前位置的接水量就是左右两边高度的较小者减去当前位置柱子的高度
js2030code
2023/01/05
6090
搞定大厂算法面试之leetcode精讲13.单调栈
搞定大厂算法面试之leetcode精讲13.单调栈 视频讲解(高效学习):点击学习 目录: 1.开篇介绍 2.时间空间复杂度 3.动态规划 4.贪心 5.二分查找 6.深度优先&广度优先 7.双指针 8.滑动窗口 9.位运算 10.递归&分治 11剪枝&回溯 12.堆 13.单调栈 14.排序算法 15.链表 16.set&map 17.栈 18.队列 19.数组 20.字符串 21.树 22.字典树 23.并查集 24.其他类型题 239. 滑动窗口最大值 (hard) 方法1.优先队列 动画过大,点击查
全栈潇晨
2021/12/01
8190
单调栈——739. 每日温度
给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。
向着百万年薪努力的小赵
2022/11/20
3050
单调栈——739. 每日温度
栈 数据结构_单调栈和单调队列
笔者在做leetcode的题(下一个出现的最大数字)时,接触到了单调栈这一种数据结构,经过研究之后,发现单调栈在解决某些问题时出奇的好用,下面是对单调栈的性质和一些典型题目。
全栈程序员站长
2022/08/02
5390
栈 数据结构_单调栈和单调队列
单调栈
 给你一个数组,要求你找到数组中每个元素左边第一个比他大的数和右边第一个比它大的数,举个例子,数组[3,5,2,1,6],3左边比他大的没有,右边比他大的是5;5左边比它大的没有,右边比他大的是6;2左边比它大的是5,右边比他大的是6;1左边比他大的是2,右边比他大的是6;6左边比他大的没有,右边比它大的没有
mathor
2018/08/17
5080
单调栈巧解柱状图最大矩形
这几天群里打卡的几道题都是十分经典的面试题,经典是因为这些题都是一题多解的。在这些高效的解法中,单调栈是一个很有技巧的解法,所以这一次我们来聊聊这个单调栈。
TechFlow-承志
2020/03/06
1.6K0
详解单调栈算法
如果你对这篇文章可感兴趣,可以点击「【访客必读 – 指引页】一文囊括主页内所有高质量博客」,查看完整博客分类与对应链接。
全栈程序员站长
2022/11/09
6940
详解单调栈算法
单调栈——42. 接雨水——面大厂必须会的困难题
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
向着百万年薪努力的小赵
2022/11/20
1940
单调栈——42. 接雨水——面大厂必须会的困难题
经典接雨水【单调栈】【动态规划】
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
韩旭051
2021/04/14
7890
经典接雨水【单调栈】【动态规划】
单调栈总结_进栈和出栈的算法思想
单调栈是一种特殊的栈,特殊之处在于栈内的元素都保持一个单调性。 假设下图是一个栈内元素的排列情况(单调递增的栈):
全栈程序员站长
2022/11/09
3460
单调栈总结_进栈和出栈的算法思想
力扣每日一刷(2023.9.24)
本题其实可以使用暴力解法实现的, 但是力扣的时间复杂度分析的有问题, O(n2)的时间复杂度尽然过不了。
用户11097514
2024/05/31
1190
力扣每日一刷(2023.9.24)
相关推荐
单调栈,栈还能单调一下?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验