初始单调栈

文章介绍了如何使用单调栈解决力扣中的两道问题——739.每日温度和496.下一个更大元素I。首先阐述了暴力解法的不足,然后详细解释了单调栈的思路,包括栈的初始化、元素的入栈和出栈条件,以及如何找到目标元素。最后展示了具体代码实现。

单调栈:

指栈中元素的值都是单调递增或者递减的,递增的可以求在数组中第一个比自己大的元素,递减可以用来求第一个比自己小的元素。

739. 每日温度 - 力扣(LeetCode)

今天刷的题目有点多,一时间看题目有点分不清楚了。

思路:这种题一眼就能想到暴力解法,比如用两层for循环,只要找到一个值大于当前值,就将该值赋给当前的数组。当然是肯定会超时的。

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        vector<int>answer(temperatures.size(), 0);
        //暴力解法是肯定会超出时间的
        for (int i = 0; i < temperatures.size();i++)
        {
            for (int j = i + 1; j < temperatures.size(); j++)
            {
                if (temperatures[j] > temperatures[i])//只要找到有比当前大的,就可以让j-i赋值给我
                {
                    answer[i] = j - i;
                    break;
                }
            }
        }
        return answer;
    }
};

单调栈解法思路:

定义一个栈,并初始化单调栈,将第一个元素下标插入栈中,只要方便在后面用来取单调栈的元素。

1、定义单调栈,明确单调栈的下际的含义

2、初始化当前栈

3、确定遍历顺序

4、明确出栈和入栈

因为是要寻找每一个元素比当前元素大的下标,也就是在第几天后会出现。

有两种情况,如果当前温度高于我需要遍历的,那么就是返回当前值的结果减去需要寻找的下标的值。

answer[st.top()] = i - st.top();

找到当前的值之后,要将该值弹出,这样才能够让后面的值继续判断,并且当前循环结束,要将nums[i]加入其中,因为栈中已经都是比我当前的值大,也就是在我之后。

如果未能找到初始值,那么也不用管当前的。因为一开始就初始化了。

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        stack<int>st;
        vector<int>answer(temperatures.size(), 0);
        st.push(0);
        for (int i = 1; i < temperatures.size(); i++)
        {
            if (temperatures[i] < temperatures[st.top()])
            {
                st.push(i);
            }
            else if (temperatures[i] == temperatures[st.top()])
            {
                st.push(i);
            }
            else {
                while (!st.empty() && temperatures[i] > temperatures[st.top()]) {
                    answer[st.top()] = i - st.top();
                    st.pop();
                }
                st.push(i);
            }
        }
        return answer;
    }
};

496. 下一个更大元素 I - 力扣(LeetCode)

思路:本题可能一开始会将其复杂化,因为要寻找nums1中元素在nums2中出现的下标,vector数组中也没用直接能够返回当前的函数,得用迭代器,然后再用当前迭代器减去开始的值。

寻找某个元素在在数组中是否出现过,是不是可以想到哈希表。

在这可以以nums1中元素作为关键词,元素下标作为值,来建立一个unordered_map哈希表。

这样在对nums2中进行寻找时,只要找到当前第一个值比当前值大,那么就对该值在nums1中进行寻找,若不能找到,则将此元素弹出,若可以,那么就让结果数值中的nums1元素的下标等于当前nums2的元素。

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        stack<int>st;
        vector<int>result(nums1.size(), -1);
        if (nums1.size() == 0)return result;

        unordered_map<int, int>umap;
        for (int i = 0; i < nums1.size(); i++)
        {
            umap[nums1[i]] = i;//采用数值为关键词,来查找,并用i作为result的下标;
        }
        st.push(0);
        for (int i = 1; i < nums2.size(); i++) {
                if (nums2[i] < nums2[st.top()]) {
                    st.push(i);
                }
                else if (nums2[i] == nums2[st.top()]) {
                    st.push(i);
                }
                else {
                    //妙就妙在这,如果存在有比当前大的,那么就要判断当前栈顶的元素,是否在nums1中出现过,若出现过,则可以进行操作,若没有,直接弹出
                    while (!st.empty() && nums2[i] > nums2[st.top()]) {
                        if (umap.count(nums2[st.top()]) > 0) { // 看map里是否存在这个元素
                            int index = umap[nums2[st.top()]]; // 根据map找到nums2[st.top()] 在 nums1中的下标
                            result[index] = nums2[i];//让当前result下标为index的值等于nums[i];
                        }
                        st.pop();
                    }
                    st.push(i);
                } 
        }
        return result;
    }
};

<think>嗯,用户想了解单调栈的实现和应用场景。从引用资料来看,之前已经提供了相关说明,但用户可能希望更系统化的总结。用户可能是算法学习者,正在准备面试或刷题,需要清晰且可直接应用的实例。注意到引用[1][2][3]都强调了单调栈的核心特点:保持栈内元素单调性,用于高效查找邻近更大/更小元素。引用[4][5]给出了经典题目(柱状图最大矩形)的代码实现,这些可以拆解说明。实现部分要突出三点:单调性维护(递增/递减栈)、出栈操作的含义(当前元素触发计算)、存储索引而非值的原因(方便计算边界)。应用场景需要区分典型问题类型:边界搜索、面积优化、跨度计算。用户可能特别关注“为什么用栈而不用其他结构”——需要解释后进先出特性与“最近邻”问题的适配性。代码示例选用C++和Python双版本,因为引用[4]是C++而用户历史问题含Python代码。注意在解释部分变量时保持一致性(如stk数组的top指针)。性能要明确O(n)的原因,避免用户误解退化情况。用户没提语言偏好,但上次回复含Python代码,所以优先Python示例。应用场景补充“矩阵最大矩形”的转化思路(引用[4]标题提到),这是单调栈的进阶用法,能展示该算法的扩展性。</think> 单调栈是一种特殊的数据结构,其核心在于**维护栈内元素的单调性**(单调递增或递减)。它能够高效解决一系列需查找元素“邻近边界”的问题,以下从实现和应用场景两方面详细说明: --- ### **一、单调栈实现** #### **核心思路** 1. **遍历顺序**:通常从左到右遍历数组 2. **栈内维护**:保持栈底到栈顶的单调性(增/减) 3. **出栈条件**:当新元素破坏单调性时,弹出栈顶元素并计算相关解 #### **代码示例(Python)** ```python def monotonic_stack(nums): stack = [] # 存储数组索引(通常比存值更灵活) res = [-1] * len(nums) # 初始化结果数组(例如存右侧第一个更大元素) for i in range(len(nums)): # 当栈非空且当前元素 > 栈顶元素(递减栈则用 <) while stack and nums[i] > nums[stack[-1]]: idx = stack.pop() # 弹出栈顶索引 res[idx] = nums[i] # 当前元素即为栈顶元素的"右侧更大元素" stack.append(i) # 当前元素索引入栈 return res # 示例:输入 [3, 1, 4, 2] # 输出: [4, 4, -1, -1] 表示:3右侧第一个大的是4,1右侧第一个大的是4,4和2右侧无更大元素 ``` #### **关键点** - **时间复杂为 $O(n)$**:每个元素最多入栈、出栈一次 - **栈中存储索引**:便于计算距离、区间宽度等 - **单调性选择**: - **递减栈**:找右侧第一个更大元素 - **递增栈**:找右侧第一个更小元素(如柱状图最大矩形) --- ### **二、经典应用场景** #### **1. 边界查找问题** - **下一个更大/小元素**(Next Greater Element) - 例:`[2,1,2,4,3]` → 下一个更大元素 `[4,2,4,-1,-1]` [^1][^5] - **左侧第一个更大/小元素**(类似反向遍历) #### **2. 面积优化问题** - **柱状图中最大矩形**(LeetCode 84)[^4] - **核心代码片段**(递增栈计算宽度): ```python stack = [-1] # 哨兵节点 max_area = 0 for i in range(len(heights)): while stack[-1] != -1 and heights[stack[-1]] > heights[i]: h = heights[stack.pop()] w = i - stack[-1] - 1 # 宽度 = 当前索引 - 新栈顶索引 - 1 max_area = max(max_area, h * w) stack.append(i) ``` - **最大矩形(01矩阵)**(LeetCode 85)[^4] - 转化为逐行的柱状图问题 #### **3. 跨度计算问题** - **每日温度**(LeetCode 739)[^5] - 求等待更暖天数的间隔:`[73,74,75,71,69,76]` → `[1,1,4,2,1,0]` - 解法:递减栈记录温度下标,出栈时计算天数差 #### **4. 删除重复字母(字典序最小)** - **关键点**:维护递增栈,处理字符最后出现位置 [^2] --- ### **单调栈适用条件** - 问题需找到**某一侧最近的边界元素**(更大/更小) - 计算过程需要**利用弹出元素的中间结果**(如面积、距离) - 数组遍历方向与边界查找方向一致(左→右找右边界,右→左找左边界) > 注:实际应用中,80% 单调栈问题可归结为以上场景。理解“单调性维护”和“边界触发计算”两个核心即可快速识别解法[^3][^5]。 --- **
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值