算法基础篇(13)单调栈

1、单调栈的定义

单调栈,顾名思义,就是具有单调性的栈。因此,它依旧是一个栈结构,只不过里面存储的数据是严格递增或者严格递减的。这种结构是很容易实现的(如下面代码)

cpp 复制代码
#include <iostream>
#include <stack>
using namespace std;

const int N = 3e6 + 10;
int a[N], n;

// 维护一个单调递增的栈
void test1()
{
	stack<int> st;
	for(int i = 1; i <= n; i++)
	{
		// 栈里面大于等于a[i]的元素全部出栈
		while(st.size() && st.top() >= a[i])
			st.pop();
			
		st.push(a[i]);
	}
}

// 维护一个单调递减的栈
void test2()
{
	stack<int> st;
	for(int i = 1; i <= n; i++)
	{
		// 栈里面小于等于a[i]的元素全部出栈
		while(st.size() && st.top() <= a[i])
			st.pop();
			
		st.push(a[i]);
	}
}

那么,维护一个单调栈的意义是什么?

2、单调栈解决的问题

单调栈能帮我们解决下面四个问题:

·寻找当前元素左侧,离它最近,并且比它大的元素在哪;

·寻找当前元素左侧,离它最近,并且比它小的元素在哪;

·寻找当前元素右侧,离它最近,并且比它大的元素在哪;

·寻找当前元素右侧,离它最近,并且比它小的元素在哪。

虽然是四个问题,但是原理是一致的。所以,只要解决一个,举一反三就可以解决剩下的几个。

2.1 寻找当前元素左侧,离它最近,并且比它大的元素在哪

从左往右 遍历元素,构造一个单调递减的栈。插入当前元素时:如果栈为空,则左侧不存在比当前元素大的数;如果栈非空,插入当前位置元素时的栈顶元素就是所要找的元素。

注意:因为我们要找的是最终结果的位置。因此,栈里面存的是每个元素的下标。

cpp 复制代码
#include <iostream>
#include <stack>
using namespace std;

const int N = 3e6 + 10;

int a[N], n;
int ret[N];

void test()
{
	stack<int> st;
	for(int i = 1; i <= n; i++)
	{
		while(st.size() && a[st.top()] <= a[i])
		{
			st.pop();
		}
		
		if(st.size())
		{
			ret[i] = st.top();
		}
		
		st.push(i);
	}
}

2.2 寻找当前元素左侧,离它最近,并且比它小的元素在哪

从左往右遍历元素,构造一个单调递增的栈。插入当前元素时:如果栈为空,则左侧不存在比当前元素小的数;如果栈非空,那么栈顶元素就是要找的元素。

注意:因为我们要找的是最终结果的位置。因此,栈里面存的是每个元素的下标。

cpp 复制代码
#include <iostream>
#include <stack>
using namespace std;

const int N = 3e6 + 10;

int a[N], n;
int ret[N];

void test()
{
	stack<int> st;
	for(int i = 1; i <= n; i++)
	{
		while(st.size() && a[st.top()] >= a[i])
		{
			st.pop();
		}
		
		if(st.size())
		{
			ret[i] = st.top();
		}
		
		st.push(i);
	}
}

剩下两种情况原理类似,只需要修改一下遍历顺序即可。

2.3 寻找当前元素右侧,离它最近,并且比它大的元素在哪

cpp 复制代码
#include <iostream>
#include <stack>
using namespace std;

const int N = 3e6 + 10;

int a[N], n;
int ret[N];

void test()
{
	stack<int> st;
	for(int i = n; i >= 1; i--)
	{
		while(st.size() && a[st.top()] <= a[i])
		{
			st.pop();
		}
		
		if(st.size())
		{
			ret[i] = st.top();
		}
		
		st.push(i);
	}
}

2.4 寻找当前元素右侧,离它最近,并且比它小的元素在哪

cpp 复制代码
#include <iostream>
#include <stack>
using namespace std;

const int N = 3e6 + 10;

int a[N], n;
int ret[N];

void test()
{
	stack<int> st;
	for(int i = n; i >= 1; i--)
	{
		while(st.size() && a[st.top()] >= a[i])
		{
			st.pop();
		}
		
		if(st.size())
		{
			ret[i] = st.top();
		}
		
		st.push(i);
	}
}

3、单调栈的应用

cpp 复制代码
#include <iostream>
#include <stack>
using namespace std;

const int N = 1e6 + 10;
typedef long long LL;

int n;
LL h[N], v[N];
LL sum[N];

int main()
{
	cin >> n;
	for(int i = 1; i <= n; i++)
	{
		cin >> h[i] >> v[i];
	}
		
	stack<int> st1;
	for(int i = 1; i <= n; i++)
	{
		while(st1.size() && h[st1.top()] <= h[i])
		{
			st1.pop();
		}
		
		if(st1.size())
		{
			sum[st1.top()] += v[i];
		}
		
		st1.push(i);
	}
	
	stack<int> st2;
	for(int i = n; i >= 1; i--)
	{
		while(st2.size() && h[st2.top()] <= h[i])
		{
			st2.pop();
		}
		
		if(st2.size())
		{
			sum[st2.top()] += v[i];
		}
		
		st2.push(i);
	}
	
	LL ret = 0;
	for(int i = 1; i <= n; i++)
	{
		ret = max(ret, sum[i]);
	}
	cout << ret << endl;
	
	return 0;
}
相关推荐
有点。2 分钟前
C++深度优先搜索(二)
开发语言·c++·深度优先
CQU_JIAKE1 小时前
8.5【A】
数据结构·算法
小小龙学IT1 小时前
Taskflow:用一张“任务图“玩转现代 C++ 并行编程开源项
c++·开源·github
wuminyu2 小时前
JUC包的AQS与JVM的ObjectMonitor机制对比分析
java·linux·c语言·jvm·c++
Jasmine_llq2 小时前
《P13016 [GESP202506 六级] 最大因数》
数据结构·算法
就不掉头发2 小时前
C++函数模板
java·开发语言·c++
王老师青少年编程3 小时前
2026年全国青少年信息素养大赛算法应用主题赛C++赛项【决赛】模拟卷(汇总)
c++·模拟卷·2026年·青少年信息素养大赛·算法应用主题赛·决赛
思麟呀4 小时前
C++(八):文件系统库+并行算法
开发语言·c++
凉茶钱4 小时前
【数据结构】C语言实现队列
c语言·数据结构
zander2585 小时前
34. 在排序数组中查找元素的第一个和最后一个位置:用两个边界定位区间
数据结构·算法·leetcode