栈是一种特殊的表,只在表首进行插入和删除操作(表首->栈顶;表尾->栈底)(后进先出表/LIFO表)
·支持的运算:
1.StackEmpty(S):栈S是否为空(top = -1)
2.StackFull(S): 栈S是否已满
3.StackTop(S):返回栈S的栈顶元素
4.Push(x,S):元素x入栈
5.Pop(S):删除并返回栈顶元素
·应用实例:
0.后缀算术表达式求值
·关于ADT栈模型的思考题:
1.车皮编序问题:
按顺序进站,出站的轨道最多能得到多少种不同的车皮编排方案。
例如,当n=3时,最多得到5组:123,132,213,231,321
考虑对于j属于1~n中的一个数,若在此刻j要出站,那么前j-1要都出去,有B(j-1)种可能。B(n) = ∑B(j-1)*B(n-j) = 1/(n+1)×C(2n,n) (卡特兰数)
2.亲兄弟问题:
给定一个序列,输出对于每个元素i之后最近的一个不小于它的元素位置,不存在则输出-1
如序列:6,1,4,3,6,2,4, 7,3, 5
输出: 4,2,4,4,7,6,7,-1,9,-1
思路:遍历序列依次入栈,当元素i入栈时发现栈顶元素不大于它,那么此栈顶元素的亲兄弟即i,弹出栈顶元素,直到不能再更新栈后把元素i入栈。最后留在栈内没有元素来更新的则为-1。
代码:
for(int i = 0; i < n; i++)
{
tmp.id = i;
scanf("%d",&tmp.num);
while(top >= 0 && tmp.num >= stack[top].num)
{
ans[stack[top].id] = i;
top--;
}
stack[++top] = tmp;
}
while(top>=0)
{
ans[stack[top].id] = -1;
top--;
}
for(int i = 0; i < n; i++)
printf("%d ",ans[i]);
//–作业题
3.2 文档
(第一次被选中优秀作业的一题代码,咿呀好开心///)
(input入栈后有两种操作,一个是撤销,能撤销最后一次输入的字符或撤销一次恢复;另一个是恢复,能够恢复最后一次的撤销操作。可以视作每次的撤销都是把这个字符串压入一个缓存栈,恢复则是从缓存栈中弹回到原来的栈。当遇到input时恢复失效,那么就清空缓存栈。最后输出的时候因为还是遵循先入先出,所以把原栈中先弹出到另一个空栈再挨个输出。)
#include<iostream>
#include<cstdio>
#include<stack>
#include<string>
#include<cstring>
using namespace std;
stack<string> preStack;
stack<string> aftStack;
char op[15];
char word[35];
void clear()
{
while(!aftStack.empty()) aftStack.pop();
}
void print()
{
clear();
while(!preStack.empty())
{
aftStack.push(preStack.top());
preStack.pop();
}
printf("%s",aftStack.top().c_str());
aftStack.pop();
while(!aftStack.empty())
{
printf(" %s",aftStack.top().c_str());
aftStack.pop();
}
}
int main()
{
int n;
scanf("%d",&n);
while(n--)
{
scanf("%s",op);
if(op[0] == 'i')
{
scanf("%s",word);
preStack.push(word);
//清空缓存栈
clear();
}
else if(op[0] == 'c')
{
if(op[5] == 'z')
{
if(!preStack.empty())
{
string str = preStack.top();
preStack.pop();
aftStack.push(str);
}
}
else if(op[5] == 'y')
{
if(!aftStack.empty())
{
string str = aftStack.top();
aftStack.pop();
preStack.push(str);
}
}
}
}
if(preStack.empty()) printf("No output\n");
else print();
return 0;
}
3.3 最大最小
(不明就里地偷看惹泡泡的代码嘤嘤嘤。)
(思路是,由于题目中只做加法和乘法,可以发现先把加法全做了在把这些和相乘为最大,先把乘法做完再把所有乘积相加最小。所以用两个栈,一个储存和一个储存乘积。)
#include<iostream>
#include<cstdio>
#define LL __int64
#define MAX 200
#define MOD 870764322
using namespace std;
LL add[MAX+10];
LL mul[MAX+10];
int aTop = 1;
int mTop = 1;
int main()
{
int num;
char op;
scanf("%d",&num);
add[aTop] = num;
mul[mTop] = num;
while(cin >> op >> num)
{
if(op == '+')
{
add[aTop] = (add[aTop]+num)%MOD;
mul[++mTop] = num;
}
else
{
mul[mTop] = (mul[mTop]*num)%MOD;
add[++aTop] = num;
}
}
LL Max = 1;
LL Min = 0;
while(aTop) Max = (Max*add[aTop--])%MOD;
while(mTop) Min = (Min+mul[mTop--])%MOD;
printf("%I64d\n%I64d",Max,Min);
}
3.1 火车
(模拟。sIn模拟中转栈的情况,用flag记录火车进出中转栈。每次从in数组中拿出第一个火车进栈,看是否与sOut数组的栈顶相同,若相同则出栈。最后看sOut数组中是否已全部出栈,若已全部出栈则根据flag中的记录输出in/out)
#include<cstdio>
#include<iostream>
using namespace std;
char in[15];
char sIn[15];
char sOut[15];
bool flag[30]; //1为in 0为out
int i;
int cnt = 0;
int main()
{
int n;
scanf("%d %s %s",&n,in,sOut);
int iTop = -1;
int sTop = 0;
for(i = 0; i < n; i++)
{
sIn[++iTop] = in[i];
flag[cnt++] = 1;
while(sIn[iTop] == sOut[sTop])
{
flag[cnt++] = 0;
iTop--;
sTop++;
if(sTop == n) break;
}
}
if(sTop != n) printf("No\n");
else
{
printf("Yes\n");
for(i = 0; i < 2*n; i++)
{
if(flag[i]) printf("in\n");
else printf("out\n");
}
}
return 0;
}
3.4 价值
(单调栈。如果确定区间然后在区间内求最小这样是O(n^3),如果以某点为最小然后向左向右延伸这样是O(n^2),利用单调栈可以把复杂度控制在O(n)。这里维护的是一个从栈底到栈顶单调递增的栈,当元素大于栈顶时入栈;当元素小于栈顶时,栈顶元素依次出栈直到栈顶不大于该元素。
考虑对第i个元素能够向左向右延伸的位置lef和rig,开始的时候每个元素延伸范围都是本身i,当有元素要出栈时,说明i元素小于这个出栈元素,出栈元素所能向左延伸到的地方i元素也能延伸到,于是a[i].lef = stack[pen].lef,这样在入栈找好位置之后i元素向左延伸的位置就确定了下来。同时由于是递增栈,这个栈内的每个元素所能向右延伸的位置都是栈顶元素右延伸的位置。并且,当元素应要出栈的时候,说明它遇到了一个在它右侧且比它小的元素,也就是已经没可能更新它的延伸范围了,这时候计算下以它为最小的范围区间内val以更新maxval。第n+1个元素给它赋值-1是为了最后用来弹出所有的栈内元素一一计算。
最后注意下ai很大会爆int。)
#include<cstdio>
#include<iostream>
using namespace std;
#define LL __int64
#define MAX 100000
struct num
{
int val;
int lef;
int rig;
}a[MAX+10],stack[MAX+10];
LL myMax(LL a, LL b)
{
return a>b?a:b;
}
LL sum[MAX+10];
int top = 0;
int i;
LL maxval = -MAX;
LL cal(int l,int r, int minnum)
{
return (LL)minnum*(sum[r]-sum[l]+a[l].val);
}
int main()
{
int n;
scanf("%d",&n);
for(i = 1; i <= n; i++)
{
scanf("%d",&a[i].val);
sum[i] = sum[i-1]+a[i].val;
a[i].lef = i;
a[i].rig = i;
}
a[n+1].val = -1;
a[n+1].lef = n+1;
for(i = 1; i <= n+1; i++)
{
//大于等于栈顶,入栈
if(top == 0 || a[i].val >= stack[top].val)
{
stack[++top] = a[i];
continue;
}
//小于栈顶
int pen = top;
while(pen > 0 && a[i].val <stack[pen].val)
{
maxval = myMax(maxval,cal(stack[pen].lef,stack[top].rig,stack[pen].val));
a[i].lef = stack[pen].lef;
pen--;
}
top = pen;
stack[++top] = a[i];
}
printf("%I64d",maxval);
return 0;
}
//———–
泡泡题:
(每当有两个+或者两个-粘在一起的时候就可以消去,用栈模拟。)
#include<bits/stdc++.h>
using namespace std;
#define MAX 100000+7
stack<char> sta;
char c[MAX];
int main()
{
scanf("%s",c);
int len = strlen(c);
for(int i = 0; i < len; i++)
{
if(sta.empty() || sta.top() != c[i]) sta.push(c[i]);
else if(sta.top() == c[i]) sta.pop();
}
if(!sta.empty()) printf("No\n");
else printf("Yes\n");
}