面试高频算法系列第一篇章:单调栈(思路+解题步骤详解)

前言

在学习最小栈算法之前我们必须知道何为 ,何为单调栈

简单来说在JS就是一个数组 ,只拥有pushpop方法,或者只拥有shiftunshift方法。 实现先进后出 的效果。例如:

栈先添加1再添加2,此时调用栈的弹出方法,应该先弹出2,再弹出1。

如上图,因为底部被封住了。所以1 这个元素只能从上边出口出栈 ,而因为2这个元素挡着了所以1无法出栈,必须先弹出2,1才能进行出栈操作

单调栈

单调栈 是一种特殊的数据结构,其特点是栈内的元素按照一定的单调性排列 ,可以是单调递增或单调递减。就和数组排列是顺序或者逆序是一个道理。

如果你看完了之后,绝对懂了。恭喜你。准备开始造火箭了
下面是一道经典的单调栈算法题

LeetCode|739. 每日温度

任务描述

  • [1] 给定一个整数数组 temperatures ,表示每天的温度,
  • [2] 返回一个数组 answer
  • [3] 其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。
  • [4] 如果气温在这之后都不会升高,请在该位置用 0 来代替。

示例 1:

ini 复制代码
输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]

示例 2:

ini 复制代码
输入: temperatures = [30,40,50,60]
输出: [1,1,1,0]

示例 3:

ini 复制代码
输入: temperatures = [30,60,90]
输出: [1,1,0]

提示:

  • 1 <= temperatures.length <= 105
  • 30 <= temperatures[i] <= 100

看到这里可以自己先去LeetCode写写这题,大标题就是这题的链接。不会的话可以回来看题解。

代码实现思路

  1. 实现问题之前一定要明确目标,这题的问题是让你求这个数下一个比他大的数的下标-自己的下标值 ,没有则赋值0

  2. 没好的想法之前看看暴力能不能解决 ,这题显然暴力是可以实现要求的。每到一个新的值时候通过for循环 来寻找较大数的下标。ok,这题讲完了。下课(咳咳开个玩笑)当写完之后,我们就应该来思考能不能对代码进行优化

  3. 我们发现,在每次到一个新的值时,我们都要往后进行遍历寻找,有时候几个值往后找的较大值是同一个。 例如:[2,2,2,3]这个数组我们可以知道前面的三个二,后面的较大数都是3。我们进行了许多重复的查找 有没有一种方法能解决这一点呢?在讲解单调栈解决问题之前,同学们可以先去思考一下看看有没有大概的思路锻炼自己的思维,没有的话也没事我开始也没想到。

4.我们开始思考,突然灵光一闪发现单调栈好像可以实现我们想要的效果(其实不是灵光一闪,我直接看的解析说要用单调栈)。为什么可以实现呢?还是拿[2,2,2,3]举例,我们在每一次遍历遇到新的值这里进行判断,这个新的值是否能满足之前的还没有较大数的值 的要求(是否是 0 - i-1这个区间里还没有较大值的数,新的值为他们的较大值)。

每次遍历到新的值时,都对还没有较大数的值进行判断。不断更新,知道数组遍历完全,这样所有拥有较大值的数的找到了他们的较大值数下标。而还没有找到较大值的数则赋值为0.而实现上诉代码的数据结构就用单调栈,每次元素遍历时进行单调栈内的未找到最大值元素进行判断是否更新,更新完然后入栈。这里要注意的是,在使用单调栈存储元素时,因为题目要求的是下标,所以单调栈存储的时候也是要存下标

下面是代码详解

代码实现

scss 复制代码
var dailyTemperatures = function(temperatures) {
    //初始化全部为0,省去了最后为 increaseStack
    //中剩余的每个元素在result数组中添加一个 0
    let result = new Array(temperatures.length).fill(0);
    //单调递增栈
    let increaseStack = [];
    //首元素的添加
    increaseStack.push(0);
    for (let i = 1; i < temperatures.length; i++) {
    //一直更新到,发现单调栈内的最小元素都比自己大的时候停止更新
        while (increaseStack.length > 0 && temperatures[i] > temperatures[increaseStack[increaseStack.length - 1]]) {
        // 被更新的元素从单调栈内弹出,
        //因为已经找到了最近的较大值。后续不再需要操作
            let prevIndex = increaseStack.pop();
            //存储答案
            result[prevIndex] = i - prevIndex;
        }
        //遍历的元素还没开始找,所以放入单调栈中。、
        //由后面的元素来决定是否进行进行元素更新弹栈操作
        increaseStack.push(i);
    }
    //返回最终结果
    return result;
};

结语

因为是第一次写算法讲解和思路,所以可能会有些比较晦涩。如果你有疑惑的话,欢迎私信或者评论区地下留言。我都会一一回复。好了,如果喜欢这篇文章的话,请点个赞吧!后续还会更新算法讲解的哦!

相关推荐
转调8 分钟前
每日一练:地下城游戏
开发语言·c++·算法·leetcode
不穿格子衬衫36 分钟前
常用排序算法(下)
c语言·开发语言·数据结构·算法·排序算法·八大排序
wdxylb43 分钟前
使用C++的OpenSSL 库实现 AES 加密和解密文件
开发语言·c++·算法
aqua35357423581 小时前
蓝桥杯-财务管理
java·c语言·数据结构·算法
CV金科1 小时前
蓝桥杯—STM32G431RBT6(IIC通信--EEPROM(AT24C02)存储器进行通信)
stm32·单片机·嵌入式硬件·算法·蓝桥杯
sewinger1 小时前
区间合并算法详解
算法
XY.散人1 小时前
初识算法 · 滑动窗口(1)
算法
前端李易安1 小时前
Web常见的攻击方式及防御方法
前端
韬. .2 小时前
树和二叉树知识点大全及相关题目练习【数据结构】
数据结构·学习·算法
Word码2 小时前
数据结构:栈和队列
c语言·开发语言·数据结构·经验分享·笔记·算法