栈的应用:表达式求值
学完栈后,就可以利用栈来计算表达式了:
我们输入的是中缀表达式,首先我们要把它转换成逆波兰式(后缀表达式):
思想:
从左到右依次扫描中缀表达式:
1.是操作数,直接输出;
2.是’(’,入栈;
3.是’)’,取出栈顶元素放入逆波兰式,直到栈顶元素为’('时为止;
4.是操作符,与栈顶元素比较(高于栈顶元素优先级,入栈;低于栈顶元素优先级,取出栈顶元素放入逆波兰式,弹出栈顶元素,直到栈顶元素优先级低于当前操作符的优先级)。
涉及到的操作符优先级关系:* = / > + = - > ( = )
以下有关栈的部分函数我不会过多说明,这篇文章我着重写下中缀表达式转换成逆波兰式后求值,不过文末会有所有的源代码。
中缀表达式转换为逆波兰式:
int check(char ch)//确定优先级关系的函数
{
switch(ch)
{
case '+':
case '-':
return 0;
case '*':
case '/':
return 1;
}
return -1;
}
//我想的是用double数组来存放逆波兰式,方便最后的计算
void infix(char* infixexp)
{
int len;
len=strlen(infixexp);
linkstack stack=set();//创建一个空栈
for(int i=0;i<len;i++)
{
bool p=0;//为了判断执行了while循环
while(infixexp[i]>='0' && infixexp[i]<='9')
{
p=1;
postexp[k]=postexp[k]*10+(infixexp[i]-'0');//将前缀表达式里的数字字符变为真正的数字并添加到逆波兰式
i++;//进入下一个字符(一个数字可以由多个数字字符组成)
if(i>=len)
break;
}
if(p==1)
{
k++;
}
if(infixexp[i]=='(')
{
push_link(stack,infixexp[i]);//入栈
}
if(infixexp[i]==')')
{
while(is_link(stack)==false && top_link(stack)!='(')//上面思想有说明
{
switch(top_link(stack))//取栈顶元素
{
case '+':postexp[k]=-1;break;
case '-':postexp[k]=-2;break;
case '*':postexp[k]=-3;break;
case '/':postexp[k]=-4;break;
default:break;//加减乘除分别用以上数字来代替
}
k++;
pop_link(stack);//出栈
}
pop_link(stack);
}
if(infixexp[i]=='+' || infixexp[i]=='-' || infixexp[i]=='*' || infixexp[i]=='/')
{
while(is_link(stack)==false && check(infixexp[i])<=check(top_link(stack)))//判断栈是否为空及操作符优先级关系
{
switch(top_link(stack))
{
case '+':postexp[k]=-1;break;
case '-':postexp[k]=-2;break;
case '*':postexp[k]=-3;break;
case '/':postexp[k]=-4;break;
default:break;
}
pop_link(stack);
k++;
}
push_link(stack,infixexp[i]);
}
}
//把栈中元素全部出栈添加到逆波兰式中
while(is_link(stack)==false)
{
switch(top_link(stack))
{
case '+':postexp[k]=-1;break;
case '-':postexp[k]=-2;break;
case '*':postexp[k]=-3;break;
case '/':postexp[k]=-4;break;
default:break;
}
pop_link(stack);
k++;
}
for(int i=0;i<k;i++)//输出逆波兰式
cout<<postexp[i]<<" ";
}
上面完成了前缀表达式转换成逆波兰式,下面就要实现逆波兰式求值的算法:
算法思路如下:
顺序扫描逆波兰式:
1.是操作数,入栈;
2.是操作符,取出栈中两个操作数进行计算,再把结果入栈;
3.扫描结束后,栈顶元素就是前缀表达式的结果值。
逆波兰式求值的算法:
double evaluate(double exp[])//double数组可以准确地存储两数相除的结果
{
linkstack2 stack=set2();//这里用到另一个栈的创建,文章下面会有说明
for(int i=0;i<k;i++)
{
if(exp[i]>0)//大于0表示是操作数,因为操作符都是负数
{
push_link2(stack,exp[i]);//另一个入栈
}
else
{
double val1=top_link2(stack);//另一个取栈顶元素
pop_link2(stack);//另一个出栈
double val2=top_link2(stack);
pop_link2(stack);
if(exp[i]==-1)
push_link2(stack,val1+val2);
if(exp[i]==-2)
push_link2(stack,val2-val1);
if(exp[i]==-3)
push_link2(stack,val1*val2);
if(exp[i]==-4)
push_link2(stack,val2/val1);//减法和除法两数的位置要搞清楚
}
}
return top_link2(stack);
}
以上就是本篇文章的重要的两个算法!(考虑到有的博主对栈的理解还不是很深刻,我就把原代码也讲解一下)
AC代码如下:
#include <iostream>
using namespace std;
double postexp[100]={0};
int k=0;//postexp[]和k都定义的全局变量
typedef struct Node
{
char data;
struct Node* next;
}*pnode,*top,*linkstack;//结点类型、栈顶、链栈类型
linkstack set()//创建空链栈
{
linkstack top=(linkstack)malloc(sizeof(Node));
if(top!=NULL)
top->next=NULL;
else
cout<<"创建失败!"<<endl;
return top;
}
bool is_link(linkstack top)//判断一个链栈是否为空
{
if(top->next==NULL)
return true;
return false;
}
void push_link(linkstack top,char x)//进栈
{
pnode p;
p=(pnode)malloc(sizeof(Node));//申请结点空间
if(p==NULL)
return;
p->data=x;//数据域赋值
p->next=top->next;//指针域赋值
top->next=p;//改变栈顶
}
char top_link(linkstack top)
{
if(is_link(top)==true)//用上面函数判断是否为空链栈
{
cout<<"栈已空"<<endl;
return 'f';
}
else
return top->next->data;//取栈顶元素
}
void pop_link(linkstack top)//删除栈顶元素
{
pnode p;
if(is_link(top)==true)
{
cout<<"栈已空"<<endl;
return;
}
p=top->next;//p指向栈顶元素
top->next=p->next;//改变栈顶指针
free(p);//释放删除结点空间
}
//第二个栈类似第一个栈,只是数据类型改变,其他函数思想基本一致
typedef struct Node2//命名没有那么规范,还请理解
{
double data2;
struct Node2* next2;
}*pnode2,*top2,*linkstack2;
linkstack2 set2()
{
linkstack2 top2=(linkstack2)malloc(sizeof(Node2));
if(top2!=NULL)
top2->next2=NULL;
else
cout<<"创建失败!"<<endl;
return top2;
}
bool is_link2(linkstack2 top)
{
if(top->next2==NULL)
return true;
return false;
}
void push_link2(linkstack2 top2,double x)
{
pnode2 ps;
ps=(pnode2)malloc(sizeof(Node2));
if(ps==NULL)
return;
ps->data2=x;
ps->next2=top2->next2;
top2->next2=ps;
}
double top_link2(linkstack2 top)
{
if(is_link2(top)==1)
{
cout<<"栈已空"<<endl;
return 0;
}
else
return top->next2->data2;
}
void pop_link2(linkstack2 top)
{
pnode2 p;
if(is_link2(top)==1)
{
cout<<"栈已空"<<endl;
return;
}
p=top->next2;
top->next2=p->next2;
free(p);
}
int check(char ch)
{
switch(ch)
{
case '+':
case '-':
return 0;
case '*':
case '/':
return 1;
}
return -1;
}
void infix(char* infixexp)
{
int len;
len=strlen(infixexp);
linkstack stack=set();
for(int i=0;i<len;i++)
{
bool p=0;
while(infixexp[i]>='0' && infixexp[i]<='9')
{
p=1;
postexp[k]=postexp[k]*10+(infixexp[i]-'0');
i++;
if(i>=len)
break;
}
if(p==1)
{
k++;
}
if(infixexp[i]=='(')
{
push_link(stack,infixexp[i]);
}
if(infixexp[i]==')')
{
while(is_link(stack)==false && top_link(stack)!='(')
{
switch(top_link(stack))
{
case '+':postexp[k]=-1;break;
case '-':postexp[k]=-2;break;
case '*':postexp[k]=-3;break;
case '/':postexp[k]=-4;break;
default:break;
}
k++;
pop_link(stack);
}
pop_link(stack);
}
if(infixexp[i]=='+' || infixexp[i]=='-' || infixexp[i]=='*' || infixexp[i]=='/')
{
while(is_link(stack)==false && check(infixexp[i])<=check(top_link(stack)))
{
switch(top_link(stack))
{
case '+':postexp[k]=-1;break;
case '-':postexp[k]=-2;break;
case '*':postexp[k]=-3;break;
case '/':postexp[k]=-4;break;
default:break;
}
pop_link(stack);
k++;
}
push_link(stack,infixexp[i]);
}
}
while(is_link(stack)==false)
{
switch(top_link(stack))
{
case '+':postexp[k]=-1;break;
case '-':postexp[k]=-2;break;
case '*':postexp[k]=-3;break;
case '/':postexp[k]=-4;break;
default:break;
}
pop_link(stack);
k++;
}
for(int i=0;i<k;i++)
cout<<postexp[i]<<" ";
}
double evaluate(double exp[])
{
linkstack2 stack=set2();
for(int i=0;i<k;i++)
{
if(exp[i]>0)
{
push_link2(stack,exp[i]);
}
else
{
double val1=top_link2(stack);
pop_link2(stack);
double val2=top_link2(stack);
pop_link2(stack);
if(exp[i]==-1)
push_link2(stack,val1+val2);
if(exp[i]==-2)
push_link2(stack,val2-val1);
if(exp[i]==-3)
push_link2(stack,val1*val2);
if(exp[i]==-4)
push_link2(stack,val2/val1);
}
}
return top_link2(stack);
}
int main()
{
char* p;
p=(char*)malloc(sizeof(char)*100);
cout<<"输入要计算的表达式:"<<endl;
cin>>p;
cout<<"输出后缀表达式(-1为'+' -2为'-' -3为'*' -4为'/'):"<<endl;
infix(p);
cout<<endl<<"输出计算最终的结果(出现栈已空时,说明当前运算符多于数字,不用担心):"<<endl;
cout<<evaluate(postexp)<<endl;
return 0;
}
学会了求前缀表达式的结果值后,就可以尝试着开发一个小项目来做一个简易计算器,代码中有什么改善或错误的地方,请各位大佬评论指出。