数据结构9————栈的应用3-后缀表达式
文章目录
一.前言
1. 中缀表达式是什么
是一个通用的算术或逻辑公式表示方法, 操作符是以中缀形式处于操作数的中间(例:3 + 4),中缀表达式是人们常用的算术表示方法。
2. 后缀表达式(逆波兰表达式)是什么
后缀表达式,指的是不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则)。比如 3 4 +(即中缀3+4的意思)
3. 本篇博文的主要内容
- 中缀表达式转后缀表达式
- 后缀表达式的计算
4. 其他
- 和我之前其他博文一样,所以代码只贴核心代码,其他代码,如栈的运算相关的代码并没有贴出来。
- 如果想看完整代码。可以去文末的git地址下载,相关代码。
二.中缀表达式转后缀表达式
1.思路
- 从左到右遍历中缀表达式的每个数字和符号,若是数字就将计数(数字的长度);
- 若是符号,先将之前记录的数字存放到结果数组中,并判断其与栈顶符号的优先级,是右括号或优先级低于或等于栈顶符号(乘除优先加减)则栈顶元素依次出找并存放到结果数组。
- 当栈顶符号优先级高于当前符号或者空栈时,将当前符号入栈(当前符号不为‘)’)。
- 当中缀表达式遍历完后,将最后一个数字存放到结果数组中,并将符号栈里的所有符号依次出栈,并存入结果数组中。
- 输出结果数组中的所有元素。
2.代码(完整代码见文末链接)
结构体的构建
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXSIZE 40
#define TRUE 1
#define FALSE 0
typedef struct//栈内元素,存符号或者数字
{
char string[10];
} Elemtype;
typedef struct
{
Elemtype data[MAXSIZE];
int top;
}SeqStack;
优先级
int Priority(Elemtype e1){
switch(e1.string[0]){
case '+':
return 1;
case '-':
return 1;
case '*':
return 2;
case '/':
return 2;
case '(':
return 0;
case ')':
return 0;
}
}
核心代码
SeqStack *sign;//符号
Elemtype result[50];//结果
int result_top=0;
int Convert(char str[]) {
Elemtype e1,e2,e3; //e1,表达式截取的数字。e2,表达式截取的符号(待入栈)。e3符号栈栈顶符号
int i,j,count=0; //count计算数字的长度
for(i=0;str[i];i++){
if(str[i]>='0'&&str[i]<='9'){ //如果是数字
e1.string[count]=str[i];
count++;
}
else{
if(count>0){
e1.string[count]=0;
result[result_top]=e1;//数字存入结果数组中
result_top++;
}
e2.string[0]=str[i];
e2.string[1]=0;
if(str[i]=='('){ //左括号直接入栈
Push(sign,e2);
}else if(str[i]=='+'||str[i]=='-'||str[i]=='*'||str[i]=='/'||str[i]==')'){
GetTop(sign,&e3);
//和栈顶元素比较,如果比栈顶元素优先级低/相等,出栈栈顶
//直到栈空,或着当前符号比栈顶元素优先级高,或者碰到'('
while(StackEmpty(sign)==0&&Priority(e2)<=Priority(e3)&&e3.string[0]!='('){
Pop(sign,&e3);//出栈
result[result_top]=e3;//存入结果数组中
result_top++;
GetTop(sign,&e3);//取出下一个栈顶元素
}
if(e2.string[0]!=')'){//如果当前符号不是')',将符号入栈
Push(sign,e2);
}else{ //如果是')',出栈栈顶的'('符号
Pop(sign,&e3);
}
}else {
printf("错误!");
return;
}
count=0;//数字的计数器重置
}
}
//结束时
e1.string[count]=0;
result[result_top]=e1;//存入结果数组中
result_top++;
while(!StackEmpty(sign)){
Pop(sign,&e3);
result[result_top]=e3;//存入结果数组中
result_top++;
}
for(i=0;i<result_top;i++){
printf("%s ",result[i].string);
}
printf("\n");
}
int main(void){
sign=InitStack();
char str[100]="9+(3-1)*3+10/2";
//scanf("%s",str);
Convert(str);
}
三.后缀表达式的运算
1.思路
- 从左到右遍历后缀表达式(结果数组)的每个数字和符号,遇到是数字就进栈(数字栈),遇到是符号,就将处于栈(数字栈)顶两个数字出栈,进行运算,运算结果进栈(数字栈),一直到最后获得结果
- 输出数字栈里的数字(计算正确的话,栈内只有一个数组)。
2.其他
- 在完整代码版中,后缀表达式来源于前一个代码,所以后缀表达式的来源于结果数组。
- 结合这个代码和上面的代码可以直接计算算术表达式的值
3.代码(完整代码见文末链接)
结构体的构建同上
数字和字符串之间的转换
int strnum(Elemtype e1){
int sum=0,i;
for(i=0;e1.string[i];i++){
sum=sum*10+e1.string[i]-'0';
}
return sum;
}
Elemtype numstr(int sum){
Elemtype e;
char c;
int i;
for(i=0;sum;i++){
e.string[i]=sum%10+'0';
sum=sum/10;
}
e.string[i]=0;
for(i=0;i<strlen(e.string)/2;i++){
c=e.string[i];
e.string[i]=e.string[strlen(e.string)/2-i];
e.string[strlen(e.string)/2-i]=c;
}
return e;
}
核心代码
SeqStack *num;//数字栈
Elemtype result[50];//结果
void Calculate(){
int sum;
Elemtype e1,e2;
int i;
for(i=0;i<result_top+1;i++){ //是数字就入栈
if(result[i].string[0]>='0'&&result[i].string[0]<='9'){
Push(num,result[i]);
}else{ //是符号就出栈两个栈顶元素并运算 ,然后入栈
Pop(num,&e1);
Pop(num,&e2);
switch(result[i].string[0]){
case '+':
sum = strnum(e2) + strnum(e1);
break;
case '-':
sum = strnum(e2) - strnum(e1);
break;
case '*':
sum = strnum(e2) * strnum(e1);
break;
case '/':
sum = strnum(e2) / strnum(e1);
break;
}
e1=numstr(sum);
Push(num,e1);
}
}
printf("%s\n",num->data[0].string);
}
int main(void){
sign=InitStack();//创建栈
num=InitStack();
char str[100]="9+(3-1)*4+10/2";
//char str[100];
//scanf("%s",str);
Convert(str);
Calculate(result);
}
四.其他可进行的优化
- 可以进行小数优化
- 引入负号
- 可以一次遍历直接将算术表达式的结果计算出来