【单调栈】

单调栈

1.0 原始模型

描述:给定一个序列,求出序列每一个位置在左侧距离其最近的,且比起小的数,若是不存在则返回-1,存在则返回其值, 最后输出该结果。
解析:
	首先考虑对该题目的朴素解(暴力解法),挖掘一些性质,把求解空间进一步缩小,从而下降问题的求解时间复杂度。
	1. 暴力作法
	双重循环:第一重循环,遍历序列上全部的位置i;
	        第二重循环,从第一重循环的位置i开始,向左遍历,直到找到第一个比位置i处的值,不然输出-1,表示不存在这样的小值;
	        伪码以下:
	        for i = 0; i < n; i++
	        	for j = i- 1; j >= 0; j--
	        		if (a[i] > a[j]) {
	        			cout << a[j] <<endl;
	        			break;
	        		} 
	        	cout << -1 << endl;
	2.分析特性
	    能够观察,当咱们经过一些数据结构,能够保留以前遍历过的全部的元素,对于给定位置i, 那么假设在i的左侧存在两个位置j,k,知足
	    a[j] >= a[k] (j < k),由于是须要找左侧最近,因此这时选择k位置比j位置更优,对于位置i,位置k更优,所以能够舍去位置j。
	    由于上面其实是逆序的状况,要删除这种逆序状况,这样获得的序列是严格递增的。由于咱们在比较时,老是要比较左侧最近的点,所以须要使用栈的数据结构来
	    使用。
	3.代码实现
	stack st;
	int a[N];
	
	for (int i = 0; i < n; i++)
	{
		//对于位置i,须要考察期左侧的栈序列;
		while (!st.empty() && st.top() >= a[i]) 
		{  //栈非空,且比栈顶元素小,说明其能够取代栈顶成为更优解;
			st.pop();
		}
		
		//此处要么栈为空,或者当前元素比栈顶大;
		if (!st.empty()) 
		{
			//此时栈非空,那么栈顶就是其左侧比当前值还小的元素;
			cout << st.top() << endl;
		} else {
			//栈为空,说明左侧没有符合的值了,这个时候输出特殊状况处理;
			cout << -1 <<endl;
		}
		//将当前元素添加进去,由于在当前元素以前已经保持了严格递增,将当前元素添加进去后,单调性仍是保持不变
		//由于前面的if语句已经保证了,从而能够保证该性质能够求解。
		st.push(a[i]);
	}
#include <iostream>
#include <algorithm>

using namespace std;
const int N = 100010;
//这里采用的是数组模拟栈的方式,彻底可用STL的方式来写,只不过会增长一些运行时间;
int n;
int stk[N], tt;
int main()
{
    cin >> n;
    for (int i = 0; i < n; i++) {
        int x;
        cin >> x;
        while (tt && stk[tt] >= x) tt--;
        if (tt) cout << stk[tt] <<" ";
        else cout << -1 <<" ";
        
        stk[++tt] = x;
        
    }
    cout <<endl;
    return 0;
    
}

1.1 扩展

相似的,还有寻找左侧最近且最大的,右侧最近最小/最大的问题。ios

2.0参考

  1. https://www.acwing.com/video/258/