基础数据结构 ①(栈|队列|链表)
*Update on 2018/10/8
一.栈
两种实现,数组(stl选手现在弃了...)和stl自带的<stack>。
1)———算术表达式运算
我们用3*(1-2)为例
①后缀表达式(逆波兰式)即为1 2 - 3 *
建立栈,在将式子中的元素逐一扫描,若遇到数则进栈,遇到运算符则取出栈顶两元素进行计算后进栈。
②中缀表达式 即为我们人脑接受的式子
1 #include<string> 2 #include<iostream> 3 #include<cstdio> 4 #include<algorithm> 5 #include<stack> 6 using namespace std; 7 string cnt; 8 int n; 9 stack<char>res; 10 void solve(string cnt) 11 { 12 for(int i=0;i<=cnt.size()-1;i++) 13 { 14 if(cnt[i]=='<') cnt[i]='1'; 15 else if(cnt[i]=='>') cnt[i]='2'; 16 else if(cnt[i]=='(') cnt[i]='3'; 17 else if(cnt[i]==')') cnt[i]='4'; 18 else if(cnt[i]=='[') cnt[i]='5'; 19 else if(cnt[i]==']') cnt[i]='6'; 20 else if(cnt[i]=='{') cnt[i]='7'; 21 else if(cnt[i]=='}') cnt[i]='8'; 22 } 23 for(int i=0;i<=cnt.size()-1;i++) 24 { 25 if(cnt[i]<=res.top()) 26 { 27 if(cnt[i]=='1'||cnt[i]=='3'||cnt[i]=='5'||cnt[i]=='7') 28 { 29 res.push(cnt[i]); 30 } 31 else 32 { 33 printf("NO\n"); 34 return ; 35 } 36 } 37 else 38 { 39 if(cnt[i]-res.top()<=2&&cnt[i]-res.top()>=0) res.pop(); 40 else 41 { 42 printf("NO\n"); 43 return ; 44 } 45 } 46 } 47 if(res.top()<60) 48 { 49 printf("NO\n"); 50 return ; 51 } 52 printf("YES\n");return; 53 } 54 void init() 55 { 56 for(int i=1;i<=res.size();i++) 57 res.pop(); 58 } 59 int main() 60 { 61 freopen("strs.in","r",stdin); 62 freopen("strs.out","w",stdout); 63 scanf("%d",&n); 64 for(int i=1;i<=n;i++) 65 { 66 cin>>cnt; 67 res.push('~'); 68 solve(cnt); 69 init(); 70 } 71 fclose(stdin); 72 fclose(stdout); 73 return 0; 74 }
2)———求进出栈可能序列种数,进栈的顺序为1~N。
法一:最直接的想法是搜索(二进制枚举当前进栈/在栈非空的情况下出栈两种情况),但数据较大时可能会T;
法二:$O(n^2)$递推。法三:Dp。也是$O(n^2)$。
法四:
这里给出一种数学方法——卡特兰数。
若有n个元素待出栈,那么所有可能的出栈方法有C(2n,n)-C(2n,n-1)种。(C为组合数)
C(2n,n)-C(2n,n-1)为卡特兰数。其实卡特兰数有许多表示方法,这是最高效的一种。
另外,它的递推式为h(n)=h(n-1)*(4*n-2)/(n+1)。
多说一句,在一个凸多边形中,通过若干条互不相交的对角线,把这个多边形划分成了若干个三角形。凸多边形的边数n,求不同的方案数f(n),也为卡特兰数。
然后再多说一句求组合数的方法。可以输出杨辉三角。
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 using namespace std; 5 int c[2002][2002]; 6 int n,fin; 7 int main() 8 { 9 scanf("%d",&n); 10 for(int i=1;i<=2*n;i++) c[i][1]=1,c[i][i]=1; 11 for(int i=3;i<=2*n;i++) 12 { 13 for(int j=2;j<=i;j++) 14 { 15 c[i][j]=c[i-1][j-1]+c[i-1][j]; 16 } 17 } 18 fin=c[2*n][n]-c[2*n][n-1]; 19 printf("%d",fin); 20 return 0; 21 }
3)———单调栈
Poj 2559
求最大矩形的面积
思考方法:先从简单情况入手,若矩形高度单调递增,可以把每个矩形的高度作为最终矩形的高度,把宽度扩展到右边界。若突然出现一个矮的,前面矩形的一部分就没有用了,可以直接报废,弹出栈顶元素直到栈首小于等于当前,再进栈。以维护序列的递增性。
另外特判,防止最后还有多余矩形可以增加一个高度为0的矩形。
时间复杂度为O(N)。
单调栈的好处就在于及时排除不可能的选项,保持策略的高度有效秩序。--lyd
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 using namespace std; 7 int n,p; 8 long long ans; 9 int a[100005]; 10 int s[100005]; 11 int w[100005]; 12 void sta(int n,int a[]) 13 { 14 a[n+1]=0; 15 for(int i=1;i<=n+1;i++) 16 { 17 if(a[i]>s[p]) 18 { 19 s[++p]=a[i]; 20 w[p]=1; 21 } 22 else 23 { 24 int wid=0; 25 while(s[p]>a[i]) 26 { 27 wid+=w[p]; 28 ans=max(ans,(long long)s[p]*wid); 29 p--; 30 } 31 s[++p]=a[i]; 32 w[p]=wid+1; 33 } 34 } 35 cout<<ans<<endl; 36 } 37 int main() 38 { 39 while(1) 40 { 41 scanf("%d",&n); 42 if(n==0) break; 43 for(int i=1;i<=n;i++) 44 scanf("%d",&a[i]); 45 sta(n,a); 46 memset(a,0,sizeof(a)); 47 memset(s,0,sizeof(s)); 48 memset(w,0,sizeof(w)); 49 ans=0,n=0; 50 } 51 return 0; 52 }
4)对顶栈的思想
有光标移动的编辑器。用两个栈存储,1~当前光标位置存在第一个栈中、剩下的存在另一个栈中。
二、队列