單調棧
- 單調遞增棧:數據出棧的序列為單調遞增序列(比站內元素小就入棧,否則將棧中比當前元素小的元素彈出后再入棧)
- 單調遞減棧:數據出棧的序列為單調遞減序列(比站內元素大就入棧,否則將棧中比當前元素大的元素彈出后再入棧)
視野總和
描敘:有n個人站隊,所有的人全部向右看,個子高的可以看到個子低的發型,給出每個人的身高,問所有人能看到其他人發現總和是多少。
輸入:4 3 7 1
輸出:2
思路:設置一個單調遞增棧,當這個人可以被站內人看到時(即比棧內數小時)入棧,同時將棧內每個人能看到的人數加一,如果遇到了一個比棧頂元素高的人,那么棧內一些人是看不到他的,所以將這些看不到他的人出棧,出棧完成后再將棧內剩下元素能看到的人數各自加一。大致是這種思路,但是由於這題求得是總和,所以不必求出每個人能看到多少人,避免每次有元素入棧都要遍歷原有棧內的元素。可以改為在每個元素出棧時計算他能看到的人數。例如第i個人要入棧(即第i個人比s.top()這個人高時),此時棧內s.top()這個人被第i個人擋住了,第i個人后面的都看不到了,所以其出棧時看到的人數為i~s.top()之間的人。
#include<bits/stdc++.h>
using namespace std;
int FieldSum(vector<int>& v)
{
int sum=0;
v.push_back(INT_MAX);
stack<int> s;
for(int i=0;i<v.size();i++)
{
while(!s.empty()&&v[i]>v[s.top()])
{
int x=s.top();
s.pop();
sum+=(i-x-1); //i為當前元素,x為出棧元素,相減部分為x可以看到的數量;
}
s.push(i);
}
return sum;
}
int main()
{
int a[6]={8,3,2,7,1,4};
vector<int> v;
for(int i=0;i<6;i++)
{
v.push_back(a[i]);
}
cout<<FieldSum(v)<<endl;
return 0;
}
柱狀圖中最大
描述
Given n non-negative integers representing the histogram’s bar height where the width of each bar is 1,
find the area of largest rectangle in the histogram.
圖 4-1 Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].
圖 4-2 The largest rectangle is shown in the shaded area, which has area = 10 unit.
For example, Given height = [2,1,5,6,2,3], return 10.
單調棧
理解:當遍歷到第i個數時,會算出所有向左看時在數i前即包含第(i-1)個數的最大值,假設這個最大值矩形左邊邊界為left,右邊為right(即此時right+1大於left-1),這種情況下,以左邊界[left]>[right+1]但是[left-1]<[right+1],所以 這里的大小關系也是出棧的依據,以此來計算寬度,h[right]即為這一段上的最小值。
#include<bits/stdc++.h>
using namespace std;
//#define MAX 10005
int largestRectangleArea(vector<int>& heights)
{
stack<int> s;
int max=0;
heights.push_back(0); //用於處理最后一個數據
int len=heights.size();
for(int i=0;i<len;i++)
{
while(!s.empty()&&heights[s.top()]>heights[i])
{
int x=s.top();
s.pop();
//temp為寬度,如果s為空,則說明,x之前的元素都比x高,所以均可計入,寬度即為(i-1-0+1)
//s為空時,兩邊的邊界依次為下標為0,下標為i-1;
int temp=heights[x]*(s.empty()?i:(i-s.top()-1));
max=max>temp?max:temp;
}
s.push(i);
}
return max;
}
int main()
{
int a[6]={2,1,5,6,2,3};
vector<int> v;
for(int i=0;i<6;i++)
{
v.push_back(a[i]);
}
cout<<largestRectangleArea(v)<<endl;
return 0;
}
分治法
此題還可以使用分治法,記左右邊界為left,right,可以先找出一個序列中的最小值min,若最大矩形包含這個min,則最大矩形一定為(right-left+1)*min;所以可以分為(left,min-1),(min+1,right)兩個子問題尋找最大矩形,然后三種情況取最大值。(因為最大矩形連續,而第二三種情況一定不包含min,所以可以分開看成兩個無聯系的子問題)
int maxArea(int left,int right,vector<int>& v)
{
if(left==right)
return v[left];
bool sortedlr=true; //是否從左到右遞增
bool sortedrl=true; //是否從右到左遞增
int min=left;
for(int i=left;i<=right;i++)
{
if(v[i]>v[i-1])
{
sortedrl=false;
}
if(v[i]<v[i-1])
{
sortedlr=false;
}
if(v[i]<v[min])
{
min=i;
}
}
if(sortedlr)
{
int maxa=v[left]*(right-left+1);
for(int i=left+1;i<=right;i++)
{
int temp=v[i]*(right-i+1);
maxa=maxa>temp?maxa:temp;
}
return maxa;
}
if(sortedrl)
{
int maxa=v[right]*(right-left+1);
for(int i=right-1;i>=left;i--)
{
int temp=v[i]*(i-left+1);
maxa=maxa>temp?maxa:temp;
}
return maxa;
}
else
{
//繼續分治
int l=0;
if(left<min)
{
l=maxArea(left,min-1,v);
}
int r=0;
if(right>min)
{
r=maxArea(min+1,right,v);
}
int x=l>r?l:r;
int temp=v[min]*(right-left+1);
return x>temp?x:temp;
}
}
int largestRectangleArea(vector<int>& heights)
{
int left=0;
int right=heights.size()-1;
if(right==-1)
return 0;
else if(right==0)
{
return heights[0];
}
else
{
return maxArea(left,right,heights);
}
}
求最大區間
描述:給出一組數字,求一區間,使得區間元素和乘以區間最小值最大,結果要求給出這個最大值和區間的左右端點
輸入:3 1 6 4 5 2
輸出:60
3 5
解釋:將3到5(6+4+5)這段區間相加,將和與區間內最小元素相乘獲得最大數字60
思路:使用暴力解法求出所有區間,再求出區間的最小值相乘跟新數據,並不是一種很好的算法,所以經過上面倆題的磨煉,此時我們應該使用一個單調遞減棧同矩形相似,如果以一個數為該區間最小值,則算出該最小值可以構成的最大區間。
#include<bits/stdc++.h>
using namespace std;
int findMax(vector<int>& v,int &p,int &q)
{
v.push_back(0);
stack<int> s;
int max=0;
int sum;
for(int i=0;i<v.size();i++)
{
while(!s.empty()&&v[s.top()]>v[i])
{
int x=s.top();
s.pop();
sum=0;
int j;
if(s.empty())
{
//p=0;
j=0;
}
else
{
//p=s.top()+1;
j=s.top()+1;
}
for(j;j<i;j++)
{
sum+=v[j];
}
int temp=sum*v[x];
max=max>temp?max:temp;
if(max==temp)
{
p=s.empty()?0:s.top()+1;
q=i-1;
}
}
s.push(i);
}
return max;
}
int main()
{
int a[]={3,1,6,4,5,2};
vector<int> v;
int p,q;
for(int i=0;i<6;i++)
{
v.push_back(a[i]);
}
int x=findMax(v,p,q);
cout<<x<<endl;
cout<<(p+1)<<" "<<(q+1)<<endl; //p,q,為下標,輸出為序號
return 0;
}
尋找無序數組每個元素的后面第一個比它大的元素值
如題
#include<bits/stdc++.h>
using namespace std;
vector<int> nextmax(vector<int> &v)
{
stack<int> s;
vector<int> res(v.size());
int size=v.size();
int i=0;
while(i<size)
{
if(s.empty()||v[s.top()]>=v[i])
{
s.push(i++);
}
else
{
int tmp=s.top();
res[tmp]=v[i];
s.pop();
}
}
while(!s.empty())
{
res[s.top()]=INT_MAX; //將后面沒有比他更大的元素置為INT_MAXINT_MAX
s.pop();
}
return res;
}
int main()
{
int a[10]={1,2,4,2,5,76,89,3,45,34};
vector<int> v;
for(int i=0;i<10;i++)
{
v.push_back(a[i]);
}
vector<int> res=nextmax(v);
for(int i=0;i<res.size();i++)
{
cout<<res[i]<<endl;
}
return 0;
}