【單調棧】

單調棧

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/
相關文章
相關標籤/搜索