当前位置: 首页 > news >正文

算法基础 典型题 单调栈

算法基础练习总结入口:我的算法地图

文章目录

    • 一、基本概念
    • 二、场景分析
    • 三、典型题目

一、基本概念

单调栈是一种栈内元素保持严格单调性(递增或递减) 的特殊栈结构,核心作用是高效寻找数组中元素的 “前后最近的更大 / 更小元素”,从而将暴力解法的 O (n²) 时间复杂度优化为 O (n)(每个元素最多入栈和出栈一次)。其思想简洁但应用灵活,是解决 “数组元素关系” 类问题的高频算法模式。

二、场景分析

1、常见题目类型
单调栈主要分为单调递增栈和单调递减栈,适用场景不同,核心是根据 “要找更大元素还是更小元素” 选择栈类型:
在这里插入图片描述
2、使用关键技巧
1)栈存储索引而非值
索引包含位置信息,可用于计算元素之间的距离(如宽度i - st.top() - 1),而值无法直接获取位置,这是单调栈的核心技巧。
2)单调性的选择
找 “更大元素”→ 用单调递减栈(当前元素比栈顶大时,栈顶的更大元素就是当前元素);
找 “更小元素”→ 用单调递增栈(当前元素比栈顶小时,栈顶的更小元素就是当前元素)。
3)边界处理
结果数组初始化为 - 1(无目标元素时的默认值);
循环数组:通过i % n将数组 “翻倍”,避免单独处理尾部元素;
最大矩形 / 接雨水:在数组首尾加 0(或极小值),确保栈中所有元素都能被弹出处理。
4)弹出元素时的计算
单调栈的核心逻辑在 “弹出栈顶元素” 时,此时需根据问题目标计算结果(如下一个更大元素的值、矩形的宽度、雨水的体积等),这一步是区分不同问题的关键。

三、典型题目

496. 下一个更大元素 I
nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。

    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {// 思路:单调栈统计 加 哈希表记录unordered_map <int, int> hashmap; // 记录结果,key=nums, value=下一个更大元素stack <int> indexstack; // 单调递减栈(存储索引)for (int i = 0; i < nums2.size(); i++) {// 调整栈:当前元素 > 栈顶元素,栈顶元素的下一个更大是当前元素 while (!indexstack.empty() && nums2[i] > nums2[indexstack.top()]) {int top_index = indexstack.top();indexstack.pop();hashmap[nums2[top_index]] = nums2[i];}indexstack.push(i);}vector<int> result;for (auto &num : nums1) {result.push_back(hashmap.count(num) ? hashmap[num] : -1);}return result;}

503. 下一个更大元素 II
给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1 。

    vector<int> nextGreaterElements(vector<int>& nums) {// 思路:单调栈统计, 循环数组要操作两轮stack <int> indexstack; // 单调递减栈(存储索引)int len = nums.size();vector<int> result(len, -1);for (int i = 0; i < len * 2 - 1; i++) {// 调整栈:当前元素 > 栈顶元素,栈顶元素的下一个更大是当前元素 while (!indexstack.empty() && nums[i % len] > nums[indexstack.top()]) {int top_index = indexstack.top();indexstack.pop();result[top_index] = nums[i % len];}indexstack.push(i % len);}return result;}

1944. 队列中可以看到的人数
有 n 个人排成一个队列,从左到右 编号为 0 到 n - 1 。给你以一个整数数组 heights ,每个整数 互不相同,heights[i] 表示第 i 个人的高度。一个人能 看到 他右边另一个人的条件是这两人之间的所有人都比他们两人 矮 。更正式的,第 i 个人能看到第 j 个人的条件是 i < j 且min(heights[i], heights[j]) > max(heights[i+1], heights[i+2], …, heights[j-1]) 。请你返回一个长度为 n 的数组 answer ,其中 answer[i] 是第 i 个人在他右侧队列中能 看到 的 人数 。

    vector<int> canSeePersonsCount(vector<int>& heights) {// 思路:单调栈,从右往左 维护可见高度// 基本逻辑,i要看到j(j > i),需要i到j之间的元素小于height[i]]// 更快方向,右边的人是否被看到,取决于他们与当前人的高度关系// 单调栈维护右边的 “可见高度”(栈内元素保持递增),这样可以快速判断当前人能看到多少右边的人int len = heights.size(); // 存储右边的人高度(栈顶→栈底递增)stack<int> heightstack;vector<int> result(len, 0);// 从右往左遍历(先处理右边的人,再处理左边的人)for (int i = len - 1; i >= 0; i--) {int h = heights[i];// 步骤1:弹出栈中所有比当前人矮的人(这些人能被当前人看到)while (!heightstack.empty() && heightstack.top() < h) {heightstack.pop();result[i]++;}// 步骤2:如果栈不为空,栈顶的人比当前人高(当前人能看到他,且他不会被挡住)if (!heightstack.empty()) {result[i]++;}// 步骤3:将当前人高度入栈,维护栈的单调性heightstack.push(h);}return result;}

84. 柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积。

    int largestRectangleArea(vector<int>& heights) {// 思路:单调栈// 对于每个柱子最大面积(面积 = 高度 × 宽度,宽度由 “左右两边第一个比它矮的柱子” 决定)// 遍历整个数组,求得每个i位置height为高度矩形的最大面积,就能得到最大面积int len = heights.size();stack<int> stk; // 单调栈:存储柱子索引,栈内索引对应的高度严格递增int result = 0;// 末尾补0:确保最后所有柱子都能被弹出计算(0会触发剩余元素的处理)heights.push_back(0);// 遍历所有柱子(包括新增的0,索引从0到n)for (int right = 0; right <= len; right++) {// 核心逻辑:当前柱子高度 <= 栈顶柱子高度时,计算栈顶柱子的最大面积while (!stk.empty() && heights[stk.top()] >= heights[right]) {// 栈顶柱子的高度(当前计算的矩形高度)int h = heights[stk.top()];stk.pop(); // 弹出栈顶,此时栈顶变为左边界的候选// 左边界:栈为空则左边界是-1(左侧没有更矮的柱子),否则是新栈顶索引int left = stk.empty() ? -1 : stk.top(); // 计算面积:宽度 = right(右边界) - left(左边界) - 1result = max(result, (right - left - 1) * h);}stk.push(right);}return result;}

901. 股票价格跨度
设计一个算法收集某些股票的每日报价,并返回该股票当日价格的 跨度 。当日股票价格的 跨度 被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)。例如,如果未来 7 天股票的价格是 [100,80,60,70,60,75,85],那么股票跨度将是 [1,1,1,2,1,4,6] 。实现 StockSpanner 类:StockSpanner() 初始化类对象。int next(int price) 给出今天的股价 price ,返回该股票当日价格的 跨度 。

class StockSpanner {// 思路:单调栈 加额外保存连续日数量// 额外保存连续日的数量,如果今日价格不低于某个连续日,那么可以直接累加之前这个连续日的连续日。
public:StockSpanner() {}int next(int price) {int cnt = 1;while (!stk.empty() && stk.top().first <= price) {cnt += stk.top().second;stk.pop();}stk.push({price, cnt});return cnt;        }
private:stack<pair<int, int>> stk;
};
http://www.dtcms.com/a/441713.html

相关文章:

  • 人工智能赋能传统医疗设施设备改造:路径、挑战与未来展望
  • 【Java】杨辉三角、洗牌算法
  • 密码学中的Salt
  • 嵌入式硬件——基于IMX6ULL的GPT(通用定时器)实现
  • 东莞 营销网站建设互动网站如何做
  • 【pytest】使用 marker 向 fixture 传递数据
  • 从0死磕全栈之Next.js 中间件(Middleware)详解与实战
  • 用个人电脑做服务器建网站天门市基础建设网站
  • 分布式专题——26 BIO、NIO编程与直接内存、零拷贝深入辨析
  • Redisson分布式限流
  • 计算机网络-应用层协议原理
  • 分布式文件存储系统FastDFS(入门)
  • 电机控制-PMSM无感FOC控制(五)相电流检测及重构 — 单电阻采样
  • C语言底层学习(4.数据在内存中的存储)
  • 虚幻引擎UE5专用服务器游戏开发-33 在上半身播放组合蒙太奇
  • 织梦网站栏目访问目录做网站建设哪家效益快
  • 『数据结构』消失的数字
  • 鹤山网站建设易搜互联湖南seo
  • ORB_SLAM2原理及代码解析:Tracking::CreateInitialMapMonocular() 函数
  • 【Linux】System V —— 基于建造者模式的信号量
  • VScode-ESP-IDF工程函数定义无法跳转且无注释提示
  • 最新的网站建设软件标书制作员工作内容
  • JAVA SE 基础语法 —— C / 运算符
  • SSM餐饮管理系统uto0o《开发全资源(程序 / 源码 / 数据库)+ 万言论文(文末)+ 系统界面》
  • 上饶市建设厅网站中国最新消息新冠疫苗最新消息
  • 安徽省建设银行网站关于网站建设的意见
  • 免费域名建站青岛网站有限公司
  • 广东泰通建设有限公司网站东莞人才网58
  • 什么是wap网站甘家口网站建设
  • 网站接电话中国关键词官网