栈是比较简单也是比较常用的数据结构,在许多方面都有应用,如括号匹配问题、表达式求值问题、程序递归调用等等,下面就是通过栈来对这些问题进行简单的探讨。我将三种应用写在了同一个文件中,当然也是通过函数调用的方式,拆开也没有什么影响
括号匹配
给定一个字符串,其中的字符包含三种括号:花括号{ }、中括号[ ]、圆括号( )。设计算法,判断该字符串是否有效,即字符串中括号是否匹配。括号匹配要求括号必须以正确的顺序配对,如 “{ [ ] ( ) }” 或 “[ ( { } [ ] ) ]” 等为正确的格式,而 “[ ( ] )” 或 “{ [ ( ) }” 或 “( { } ] )” 均为不正确的格式。
算法思想
1.依此扫描输入的字符串
2.遇到左括号(,【,{ 都将其入栈
3.遇到右括号),】,},先判断栈是否是空的。如果栈为空,表示没有与其匹配的括号,即存在多余的右括号,直接输出不匹配。若栈非空,弹出栈顶元素与其匹配,匹配则继续扫描,不匹配输出错误结果。遇到其他字符直接跳过,进行下一轮扫描。
4.字符串扫描完毕之后,若栈非空,表示存在多余的左括号,输出不匹配,若栈空,输出匹配成功。
算法实现
/*符号栈*/
typedef struct
{
elemtype data[maxsize];
int top;
}sqstack;
void initstack(sqstack &s)
{
s.top=-1;
}
bool stackempty(sqstack s)
{
if (s.top==-1) return true;
else return false;
}
bool push(sqstack &s,elemtype e)
{
if(s.top==maxsize-1)
{
return false;
}
s.data[++s.top]=e;
return false;
}
bool pop(sqstack &s,elemtype &e)
{
if(stackempty(s)==1)
return false;
e=s.data[s.top--];
return true;
}
bool gettop(sqstack s,elemtype &e)
{
if(stackempty(s)==1)
return false;
e=s.data[s.top];
}
/*括号匹配*/
int bracketcheck()
{
sqstack s;
initstack(s);
char ch[maxsize];
cout<<"请输入待分析串 #结束"<<endl;
char x;
cin>>x;
int i=0;
while(x!='#')
{
ch[i]=x;
cin>>x;
i++;
}
int len=i;
for (i=0;i<len;i++)
{
if(ch[i]=='(' || ch[i]=='[' || ch[i]=='{')
push(s,ch[i]);
else if (ch[i]==')' || ch[i]==']' || ch[i]=='}')
{
if (stackempty(s)) {cout<<"括号匹配失败"<<endl;return 0;}
char eletop;
pop(s,eletop);
if(ch[i]==')'&&eletop !='(') {cout<<"括号匹配失败"<<endl;return 0;}
if(ch[i]==']'&&eletop !='[') {cout<<"括号匹配失败"<<endl;return 0;}
if(ch[i]=='}'&&eletop !='{') {cout<<"括号匹配失败"<<endl;return 0;}
}
else
{
continue;
}
}
if(stackempty(s)) cout<<"括号匹配成功"<<endl;
else cout<<"括号匹配失败"<<endl;
}
测试结果
表达式求值
对于一个表达式而言,我们习惯使用中缀表达式,但是在计算机程序的实现中,使用的是后缀表达式,所有对于我们输入的中缀表达式,计算机需要将其转换为后缀表达式,然后通过栈来计算表达式的值。
中缀表达式转换为后缀表达式
算法思想
这里只针对运算符为:+ - * /,界限符为()的表达式
依次扫描表达式
1.遇到 ( 直接压栈。
2.遇到除去运算符和界限符的其他符合,直接添加到后缀表达式。
3.遇到 )依次弹出栈中的运算符,直到出现 (,并将 ( 出栈。
4.遇到+ - * /时,若栈为空,将符号入栈;若栈非空,且栈顶符号为 (,该符号入栈,若栈顶符号不为( ,比较其与栈顶符号的优先级,若其优先级大于栈顶符号,将该符号入栈;若小于,则依次从栈中弹出比其优先级高或者相等的运算符到后缀表达式,直到栈为空或者遇到(为止,然后将该扫描的运算符入栈。
5.当扫描完表达式串之后,依次弹出栈中的运算符,添加到后缀表达式中
举个例子
中缀表达式为:a+b-a*((c+d)/e-f)+g
当前扫描字符 符合栈 后缀表达式 动作
a a 加入后缀表达式
+ + a 栈为空,入栈
b + ab 加入表达式
- - ab+ 栈非空,弹出栈中优先级大于等于的运算符,+ -优先级相等,弹出+加入表达式,-入栈
a - ab+a 加入表达式
* -* ab+a *优先级大于-,*入栈
( -*( ab+a (入栈
( -*(( ab+a (入栈
c -*(( ab+ac 加入后缀表达式
+ -*((+ ab+ac 栈顶为(,+入栈
d -*((+ ab+acd 加入表达式
) -*( ab+acd+ 弹出栈内符合,直到(,最后弹出(
/ -*(/ ab+acd+ 栈顶为(,/入栈
e -*(/ ab+acd+e 加入表达式
- -*(- ab+acd+e/ -小于栈顶/优先级,弹出/,直到栈顶为(,-入栈
f -*(- ab+acd+e/f 加入后缀表达式
) -* ab+acd+e/f- 弹出栈中符号,加入表达式,直到(,最后弹出(
+ + ab+acd+e/f-*- *优先级大于+,*出栈加入表达式,-优先级等于+,-出栈加入表达式,栈为空,+入栈
g + ab+acd+e/f-*-g 加入表达式
此时符合已经扫描完 ab+acd+e/f-*-g+ 弹出栈中所有符号,加入后缀表达式
算法实现
sqstack s 为符号栈
char a[maxsize] 为输入的中缀表达式
char b[maxsize] 为转换的后缀表达式
int len 中缀表达式的长度
/*符号栈*/
typedef struct
{
elemtype data[maxsize];
int top;
}sqstack;
void initstack(sqstack &s)
{
s.top=-1;
}
bool stackempty(sqstack s)
{
if (s.top==-1) return true;
else return false;
}
bool push(sqstack &s,elemtype e)
{
if(s.top==maxsize-1)
{
return false;
}
s.data[++s.top]=e;
return false;
}
bool pop(sqstack &s,elemtype &e)
{
if(stackempty(s)==1)
return false;
e=s.data[s.top--];
return true;
}
bool gettop(sqstack s,elemtype &e)
{
if(stackempty(s)==1)
return false;
e=s.data[s.top];
}
/*中缀表达式转换为后缀表达式*/
void transfer(sqstack &s,char a[maxsize],char b[maxsize],int len)
{
int j=0;
for(int i=0;i<len;i++)
{
if(a[i]!='(' && a[i]!=')' && a[i]!='+' && a[i]!='-' && a[i]!='*' && a[i]!='/') ///操作数
{
b[j]=a[i];
j++;
}
else if(a[i]=='(')
{
push(s,a[i]);
}
else if(a[i]==')')
{
char str;
if(!stackempty(s))
{
gettop(s,str);
while(stackempty(s)!=true && str!='(')
{
pop(s,str);
b[j]=str;
j++;
gettop(s,str);
}
if(str=='(')
{
pop(s,str);
}
}
else
{
cout<<"表达式错误"<<endl;
}
}
else if(a[i]=='+' ||a[i]=='-')
{
char str;
if(!stackempty(s))
{
gettop(s,str);
while(stackempty(s)!=true && str!='(')
{
pop(s,str);
b[j]=str;
j++;
if(!stackempty(s)) gettop(s,str);
}
push(s,a[i]);
gettop(s,str);
if (str=='(')
{
pop(s,str);
}
}
else
{
push(s,a[i]);
}
}
else if(a[i]=='*' ||a[i]=='/')
{
char str;
if(!stackempty(s))
{
gettop(s,str);
while(stackempty(s)!=true && str!='(')
{
if(str=='+'||str=='-') break;
pop(s,str);
b[j]=str;
j++;
if(!stackempty(s)) gettop(s,str);
}
push(s,a[i]);
gettop(s,str);
if (str=='(')
{
pop(s,str);
}
}
else
{
push(s,a[i]);
}
}
}
while(stackempty(s)==false)
{
char str;
pop(s,str);
b[j]=str;
j++;
}
cout<<"转换为后缀表达式\n";
for(int k=0;k<j;k++)
{
cout<<b[k];
}
cout<<endl;
}
结果测试
和上面分析的结果一致。
表达式转换并求值
有了前面的将中缀表达式转换为后缀表达式的函数,实现就已经非常容易了。只是在这里需要对除去运算符和界限符的其他符号做限定,需要是0-9的数字字符
算法思想
1.调用转换函数,将输入的中缀表达式转换为后缀表达式
2.扫描后缀表达式,遇到数字便压数据栈
3.遇到运算符op,便从数据栈中弹出两个数据a,b,这里需要注意运算顺序,现出栈的是右操作数,做b op a 运算,运算结果压栈
4.扫描完之后,若表达式规范,数据栈内的剩下的一个数据变为运算结果。
算法实现
/*符号栈*/
typedef struct
{
elemtype data[maxsize];
int top;
}sqstack;
void initstack(sqstack &s)
{
s.top=-1;
}
bool stackempty(sqstack s)
{
if (s.top==-1) return true;
else return false;
}
bool push(sqstack &s,elemtype e)
{
if(s.top==maxsize-1)
{
return false;
}
s.data[++s.top]=e;
return false;
}
bool pop(sqstack &s,elemtype &e)
{
if(stackempty(s)==1)
return false;
e=s.data[s.top--];
return true;
}
bool gettop(sqstack s,elemtype &e)
{
if(stackempty(s)==1)
return false;
e=s.data[s.top];
}
/*数据栈*/
typedef struct
{
int data[maxsize];
int top;
}sqstack_int;
void initstack_int(sqstack_int &s)
{
s.top=-1;
}
bool stackempty_int(sqstack_int s)
{
if (s.top==-1) return true;
else return false;
}
bool push_int(sqstack_int &s,int e)
{
if(s.top==maxsize-1)
{
return false;
}
s.data[++s.top]=e;
return false;
}
bool pop_int(sqstack_int &s,int &e)
{
if(stackempty_int(s)==1)
return false;
e=s.data[s.top--];
return true;
}
bool gettop_int(sqstack_int s,int &e)
{
if(stackempty_int(s)==1)
return false;
e=s.data[s.top];
}
/*中缀表达式转为后缀表达式*/
void transfer(sqstack &s,char a[maxsize],char b[maxsize],int len)
{
int j=0;
for(int i=0;i<len;i++)
{
if(a[i]!='(' && a[i]!=')' && a[i]!='+' && a[i]!='-' && a[i]!='*' && a[i]!='/') ///操作数
{
b[j]=a[i];
j++;
}
else if(a[i]=='(')
{
push(s,a[i]);
}
else if(a[i]==')')
{
char str;
if(!stackempty(s))
{
gettop(s,str);
while(stackempty(s)!=true && str!='(')
{
pop(s,str);
b[j]=str;
j++;
gettop(s,str);
}
if(str=='(')
{
pop(s,str);
}
}
else
{
cout<<"表达式错误"<<endl;
}
}
else if(a[i]=='+' ||a[i]=='-')
{
char str;
if(!stackempty(s))
{
gettop(s,str);
while(stackempty(s)!=true && str!='(')
{
pop(s,str);
b[j]=str;
j++;
if(!stackempty(s)) gettop(s,str);
}
push(s,a[i]);
gettop(s,str);
if (str=='(')
{
pop(s,str);
}
}
else
{
push(s,a[i]);
}
}
else if(a[i]=='*' ||a[i]=='/')
{
char str;
if(!stackempty(s))
{
gettop(s,str);
while(stackempty(s)!=true && str!='(')
{
if(str=='+'||str=='-') break;
pop(s,str);
b[j]=str;
j++;
if(!stackempty(s)) gettop(s,str);
}
push(s,a[i]);
gettop(s,str);
if (str=='(')
{
pop(s,str);
}
}
else
{
push(s,a[i]);
}
}
}
while(stackempty(s)==false)
{
char str;
pop(s,str);
b[j]=str;
j++;
}
cout<<"转换为后缀表达式\n";
for(int k=0;k<j;k++)
{
cout<<b[k];
}
cout<<endl;
}
/*表达式求值*/
void stack_figure()
{
sqstack_int m;///数据栈
sqstack s;///符号栈
initstack_int(m);
initstack(s);
char a[maxsize];
char b[maxsize];
int n;
cout<<"请输入命令 1中缀表达式转后缀表达式 2表达式转换并求值"<<endl;
cin>>n;
switch(n)
{
case 1:
{
cout<<"请输入表达式 #号结束"<<endl;
char ch;
cin>>ch;
int len=0;
while(ch!='#')
{
a[len]=ch;
len++;
cin>>ch;
}
transfer(s,a,b,len);
break;
}
case 2:
{
cout<<"请输入操作数在0-9范围的运算表达式 #号结束"<<endl;
char ch;
cin>>ch;
int len=0;
while(ch!='#')
{
a[len]=ch;
len++;
cin>>ch;
}
transfer(s,a,b,len);
int q,p;
for(int i=0;i<len;i++)
{
if(b[i]!='+' && b[i]!='-'&& b[i]!='*'&& b[i]!='/')
{
push_int(m,b[i]-'0');
}
else
{
pop_int(m,q);
pop_int(m,p);
switch(b[i])
{
case '+':
{
q=p+q;
push_int(m,q);
break;
}
case '-':
{
q=p-q;
push_int(m,q);
break;
}
case '*':
{
q=p*q;
push_int(m,q);
break;
}
case '/':
{
q=p/q;
push_int(m,q);
break;
}
}
}
}
pop_int(m,q);
cout<<"最终结果为:"<<q<<endl;
break;
}
}
}
测试结果
递归
若有这样一个程序:
Pn(x)= 1 if n=0;
Pn(x)= 2x if n=1;
Pn(x)= 2xPn-1(x)-2(n-1)Pn-2(x) if n>=2;
算法思想
1.定义一个栈,栈内包括两个变量,一个记录n,一个为value值
2.对于递归出口,设置两个变量记录初值val0,val1
3.对于非递归出口,逐层进行压栈,直到n刚好大于递归出口
4.逐层出栈,每出栈一次,进行一次运算,得到运算结果data;并且更新val0,val1,将val1赋值给val0,data赋值给val1.
5.当栈为空的时候,val1便是运算结果。
算法原理
以举例的程序为例:
P2=2xP1-P0
P3=2xP2-4P1
P4=2xP3-6P2
发现规律了没?
没有的话我们忽略那些细节。
data val1 val0
P2 P1 P0
P3 P2 P1
P4 P3 P2
P2是我们第一次运算,也是栈顶的元素
按照我们的逻辑,将其对应
P2也就是data =val1 op val0
当要计算P3的时候,P1是不是要变成P2,P0是不是要变成P1?
所以我们要将运算得到的P2(data)赋值给P1(val1),P1赋值给P0(val0)。每一次运算都进行如上操作,最后得到的结果便保存在val1中。
当然,若n==0,则直接输出val0=1了。
算法实现
/*递归栈*/
/*说是栈,其实也就是结构体数组*/
typedef struct
{
double data;
int number;
}st[maxsize];
/*递归算法转变为非递归算法*/
void feidigui()
{
cout<<"将如下递归程序转变为非递归程序:\n";
cout<<"Pn(x)=: 1 if n==0\n";
cout<<" 2x if n==1\n";
cout<<" 2xPn-1(x)-2(n-1)Pn-2(x)\n";
cout<<"请输入n x\n";
int n,x;
cin>>n>>x;
st s;
int top=-1;///指代栈顶
double val0=1,val1=2*x; ///n=1,0时候的值
/*将n>=2时的Pn入栈*/
for(int i=n;i>=2;i--)
{
top++;
s[top].number=i;
}
/*出栈所有的Pn,逐层计算*/
while(top>=0)
{
s[top].data=2*x*val1-2*(s[top].number-1)*val0;
val0=val1;
val1=s[top].data;
top--;
}
if(n==0) cout<<val0<<endl;
else cout<<"结果为: "<<val1<<endl;
}
测试结果
完整代码
#include<iostream>
#include<bits/stdc++.h>
#define elemtype char
using namespace std;
#define maxsize 20
/*符号栈*/
typedef struct
{
elemtype data[maxsize];
int top;
}sqstack;
void initstack(sqstack &s)
{
s.top=-1;
}
bool stackempty(sqstack s)
{
if (s.top==-1) return true;
else return false;
}
bool push(sqstack &s,elemtype e)
{
if(s.top==maxsize-1)
{
return false;
}
s.data[++s.top]=e;
return false;
}
bool pop(sqstack &s,elemtype &e)
{
if(stackempty(s)==1)
return false;
e=s.data[s.top--];
return true;
}
bool gettop(sqstack s,elemtype &e)
{
if(stackempty(s)==1)
return false;
e=s.data[s.top];
}
/*数据栈*/
typedef struct
{
int data[maxsize];
int top;
}sqstack_int;
void initstack_int(sqstack_int &s)
{
s.top=-1;
}
bool stackempty_int(sqstack_int s)
{
if (s.top==-1) return true;
else return false;
}
bool push_int(sqstack_int &s,int e)
{
if(s.top==maxsize-1)
{
return false;
}
s.data[++s.top]=e;
return false;
}
bool pop_int(sqstack_int &s,int &e)
{
if(stackempty_int(s)==1)
return false;
e=s.data[s.top--];
return true;
}
bool gettop_int(sqstack_int s,int &e)
{
if(stackempty_int(s)==1)
return false;
e=s.data[s.top];
}
/*递归栈*/
typedef struct
{
double data;
int number;
}st[maxsize];
/*括号匹配*/
int bracketcheck()
{
sqstack s;
initstack(s);
char ch[maxsize];
cout<<"请输入待分析串 #结束"<<endl;
char x;
cin>>x;
int i=0;
while(x!='#')
{
ch[i]=x;
cin>>x;
i++;
}
int len=i;
for (i=0;i<len;i++)
{
if(ch[i]=='(' || ch[i]=='[' || ch[i]=='{')
push(s,ch[i]);
else if (ch[i]==')' || ch[i]==']' || ch[i]=='}')
{
if (stackempty(s)) {cout<<"括号匹配失败"<<endl;return 0;}
char eletop;
pop(s,eletop);
if(ch[i]==')'&&eletop !='(') {cout<<"括号匹配失败"<<endl;return 0;}
if(ch[i]==']'&&eletop !='[') {cout<<"括号匹配失败"<<endl;return 0;}
if(ch[i]=='}'&&eletop !='{') {cout<<"括号匹配失败"<<endl;return 0;}
}
else
{
continue;
}
}
if(stackempty(s)) cout<<"括号匹配成功"<<endl;
else cout<<"括号匹配失败"<<endl;
}
/*中缀表达式转为后缀表达式*/
void transfer(sqstack &s,char a[maxsize],char b[maxsize],int len)
{
int j=0;
for(int i=0;i<len;i++)
{
if(a[i]!='(' && a[i]!=')' && a[i]!='+' && a[i]!='-' && a[i]!='*' && a[i]!='/') ///操作数
{
b[j]=a[i];
j++;
}
else if(a[i]=='(')
{
push(s,a[i]);
}
else if(a[i]==')')
{
char str;
if(!stackempty(s))
{
gettop(s,str);
while(stackempty(s)!=true && str!='(')
{
pop(s,str);
b[j]=str;
j++;
gettop(s,str);
}
if(str=='(')
{
pop(s,str);
}
}
else
{
cout<<"表达式错误"<<endl;
}
}
else if(a[i]=='+' ||a[i]=='-')
{
char str;
if(!stackempty(s))
{
gettop(s,str);
while(stackempty(s)!=true && str!='(')
{
pop(s,str);
b[j]=str;
j++;
if(!stackempty(s)) gettop(s,str);
}
push(s,a[i]);
gettop(s,str);
if (str=='(')
{
pop(s,str);
}
}
else
{
push(s,a[i]);
}
}
else if(a[i]=='*' ||a[i]=='/')
{
char str;
if(!stackempty(s))
{
gettop(s,str);
while(stackempty(s)!=true && str!='(')
{
if(str=='+'||str=='-') break;
pop(s,str);
b[j]=str;
j++;
if(!stackempty(s)) gettop(s,str);
}
push(s,a[i]);
gettop(s,str);
if (str=='(')
{
pop(s,str);
}
}
else
{
push(s,a[i]);
}
}
}
while(stackempty(s)==false)
{
char str;
pop(s,str);
b[j]=str;
j++;
}
cout<<"转换为后缀表达式\n";
for(int k=0;k<j;k++)
{
cout<<b[k];
}
cout<<endl;
}
/*表达式求值*/
void stack_figure()
{
sqstack_int m;///数据栈
sqstack s;///符号栈
initstack_int(m);
initstack(s);
char a[maxsize];
char b[maxsize];
int n;
cout<<"请输入命令 1中缀表达式转后缀表达式 2表达式转换并求值"<<endl;
cin>>n;
switch(n)
{
case 1:
{
cout<<"请输入表达式 #号结束"<<endl;
char ch;
cin>>ch;
int len=0;
while(ch!='#')
{
a[len]=ch;
len++;
cin>>ch;
}
transfer(s,a,b,len);
break;
}
case 2:
{
cout<<"请输入操作数在0-9范围的运算表达式 #号结束"<<endl;
char ch;
cin>>ch;
int len=0;
while(ch!='#')
{
a[len]=ch;
len++;
cin>>ch;
}
transfer(s,a,b,len);
int q,p;
for(int i=0;i<len;i++)
{
if(b[i]!='+' && b[i]!='-'&& b[i]!='*'&& b[i]!='/')
{
push_int(m,b[i]-'0');
}
else
{
pop_int(m,q);
pop_int(m,p);
switch(b[i])
{
case '+':
{
q=p+q;
push_int(m,q);
break;
}
case '-':
{
q=p-q;
push_int(m,q);
break;
}
case '*':
{
q=p*q;
push_int(m,q);
break;
}
case '/':
{
q=p/q;
push_int(m,q);
break;
}
}
}
}
pop_int(m,q);
cout<<"最终结果为:"<<q<<endl;
break;
}
}
}
/*递归算法转变为非递归算法*/
void feidigui()
{
cout<<"将如下递归程序转变为非递归程序:\n";
cout<<"Pn(x)=: 1 if n==0\n";
cout<<" 2x if n==1\n";
cout<<" 2xPn-1(x)-2(n-1)Pn-2(x)\n";
cout<<"请输入n x\n";
int n,x;
cin>>n>>x;
st s;
int top=-1;///指代栈顶
double val0=1,val1=2*x; ///n=1,0时候的值
/*将n>=2时的Pn入栈*/
for(int i=n;i>=2;i--)
{
top++;
s[top].number=i;
}
/*出栈所有的Pn,逐层计算*/
while(top>=0)
{
s[top].data=2*x*val1-2*(s[top].number-1)*val0;
val0=val1;
val1=s[top].data;
top--;
}
if(n==0) cout<<val0<<endl;
else cout<<"结果为: "<<val1<<endl;
}
int main()
{
int x;
cout<<"栈的应用\n请输入命令0退出 1括号匹配 2表达式求值 3递归"<<endl;
while(cin>>x)
{
switch(x)
{
case 0:{return 0;}
case 1:{bracketcheck();break;}
case 2:{stack_figure();break;}
case 3:
{
feidigui();
break;
}
}
cout<<"\n\n\n\n请输入命令0退出 1括号匹配 2中缀表达式转换+求值 3递归"<<endl;
}
return 0;
}