算法基础篇(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;
}
相关推荐
南境十里·墨染春水7 小时前
C++ 笔记 thread
java·开发语言·c++·笔记·学习
南境十里·墨染春水7 小时前
C++ 笔记 高级线程同步原语与线程池实现
java·开发语言·c++·笔记·学习
瞎折腾啥啊7 小时前
CMake FetchContent与ExternalProject
c++·cmake·cmakelists
三品吉他手会点灯8 小时前
STM32 VSCode 开发-C/C++的环境配置中,找不到C/C++: Edit Configurations选项
c语言·c++·vscode·stm32·单片机·嵌入式硬件·编辑器
6Hzlia9 小时前
【Hot 100 刷题计划】 LeetCode 287. 寻找重复数 | C++ 数组判环 (快慢指针终极解法)
c++·算法·leetcode
Robot_Nav10 小时前
DPMPC-Planner:复杂静态环境与动态障碍物下的无人机实时轨迹规划框架
c++·无人机·mpc
码完就睡10 小时前
数据结构——栈和队列的相互模拟
数据结构
iiiiyu10 小时前
常用API(SimpleDateFormat类 & Calendar类 & JDK8日期 时间 日期时间 & JDK8日期(时区) )
java·大数据·开发语言·数据结构·编程语言
故事和你9110 小时前
洛谷-数据结构1-4-图的基本应用2
开发语言·数据结构·算法·深度优先·动态规划·图论
水饺编程11 小时前
第5章,[标签 Win32] :获取设备环境的信息
c语言·c++·windows·visual studio