算法基础篇(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;
}
相关推荐
草莓熊Lotso3 分钟前
【Linux网络】深入理解 HTTP 协议(一):从基础概念到 URL 编码解码
linux·网络·c++·网络协议·http·软件工程
眠りたいです3 分钟前
现代C++:C++17中的新语言特性
开发语言·c++·c++17
一只旭宝5 分钟前
【C++入门精讲17】序列容器
开发语言·c++
郝学胜-神的一滴12 分钟前
Qt 高级开发 021:零基础吃透 QVBoxLayout 垂直布局
开发语言·c++·qt·程序人生·用户界面
basketball61615 分钟前
C++进阶:2. std::move 和 std::forward 函数
java·开发语言·c++
玖釉-16 分钟前
LeetCode Hot 100 知识点总结与算法指南
c++·windows·算法·leetcode
Hall_IC19 分钟前
LSM6DS3TR-C现货询价丨粤科源兴ST代理商,专业FAE技术支持
c++
进击的荆棘20 分钟前
优选算法——队列+宽搜
数据结构·c++·算法·leetcode·bfs·队列
Irissgwe21 分钟前
STL简介
c++·stl