后缀表达式,又称逆波兰式
指的是不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则)。
例如:后缀表达式为“2 3 + 4 x 5 -”计算过程:
1)从左向右扫描,遇到数字就压入堆栈。(这里将 2 3 压入堆栈)。
2)遇到运算符号‘+’,压出栈顶开始的两个元素,计算 3 + 2 的值为5。将5再次压入堆栈。
3)遇到数字4,压入堆栈。
4)遇到‘x’,将栈顶开始的元素压出堆栈并计算。(计算 4 x 5 = 20)。将20压入堆栈。
5)遇到数字5,压入堆栈。
6)最后是‘-’,压出对战中的两个元素 5 20.做计算20 - 5 得到结果为15。
中缀表达式(或中缀记法)
是一个通用的算术或逻辑公式表示方法, 操作符是以中缀形式处于操作数的中间(例:3 + 4),中缀表达式是人们常用的算术表示方法。
那么怎么将中缀表达式转化为后缀表达式呢?
1)创建一个堆栈
2)从左往右顺序获取中缀表达式
2.1)遇到数字,直接输出
2.2)运算符
a:遇到左括号 '(' 直接入栈
b:遇到右括号 '(',将堆栈中左括号之前的元素全部压出堆栈。(PS:同时左括号也压出堆栈,但是我们并不打印输出)
c:遇到乘号 'x' 或者除号 '/' 直接入栈,直到遇到比它优先级更低的运算符,并以此压出堆栈。
d:遇到加号 '+' 或者减号 '-' 。如果此时是空栈,那么直接压入堆栈,否则,需要将栈中优先级高的运算符号依次压出堆栈(这里需要注意的是,如果堆栈中除了比它优先级高的运算符之外还有同等优先级的运算符,也要依次压出堆栈)直到栈变为空或者遇到左括号为止。
e:最后,获取完毕,将剩余的运算符号依次压出堆栈。
例如:将一个中缀表达式"1+((2+3)x4)-5"转化为后缀表达式的过程如下:
| 扫描到的元素 | 后缀表达式堆栈S2 (栈底-> 栈顶) | 符号堆栈S1 (栈底-> 栈顶) | 解释 |
|---|---|---|---|
| 1 | 1 | NULL | 数字,压入堆栈S2 |
| + | 1 | + | S1为空,运算符压入堆栈 |
| ( | 1 | + ( | 左括号,压入堆栈S1 |
| ( | 1 | + ( ( | 左括号,压入堆栈S1 |
| 2 | 1 2 | + ( ( | 数字,压入堆栈S2 |
| + | 1 2 | + ( ( + | S1为左括号,直接入栈S1 |
| 3 | 1 2 3 | + ( ( + | 数字,压入堆栈S2 |
| ) | 1 2 3 + | + ( | 右括号,弹出第一个左括号之前运算符 |
| x | 1 2 3 + | + ( x | S1栈顶为左括号,运算符压入堆栈S2 |
| 4 | 1 2 3 + 4 | + ( x | 数字,压入堆栈S2 |
| ) | 1 2 3 + 4 x | + | 右括号,弹出第一个左括号之前运算符 |
| - | 1 2 3 + 4 x + | - | 与 + 同优先级,因此压出+,再压入 - |
| 5 | 1 2 3 + 4 x + 5 | - | 数字,压入堆栈S2 |
| 扫描结束 | 1 2 3 + 4 x + 5 - | NULL | 结果 |
所以,最终的后缀表达式结果为:'1 2 3 + 4 x + 5 -'。
代码实现:
首先,定义一个堆栈。
typedef char ElementType;
typedef struct SNode *Stack;
#define INITSIZE 20
#define LEN sizeof (ElementType)
struct SNode{
ElementType *base;指向最底
ElementType *top;
int StackSize;
};
创建堆栈
void CreateStack(Stack S){
S->base=(ElementType *)malloc(LEN*INITSIZE);//直接将S的base指向一个字符数组
assert(S->base!=NULL);
S->top=S->base;
S->StackSize=INITSIZE;
}
压入堆栈
void Push(Stack S,ElementType X){
if(S->top - S->base>=S->StackSize){
S->base=(ElementType*) realloc(S->base,sizeof (ElementType)*(S->StackSize+10));//空间不够就补加
assert(S->base!=NULL);
S->top=S->base+S->StackSize;
S->StackSize+=10;
}
*S->top=X;
S->top++;
}
返回堆栈长度
int StackLength(Stack S){
return (S->top - S->base);
}
压出堆栈
int Pop(Stack S,ElementType *X){//传入的是一个地址
if(!StackLength(S)){
printf("栈为空\n");
return 0;
}
S->top--;
*X=*S->top;
return 1;
}
中缀表达式转化为后缀表达式
void Change(Stack S,ElementType str[]){//核心函数
int i=0;
ElementType X;
CreateStack(S);
while (str[i]!=' '){
while (isdigit(str[i])){
printf("%c",str[i++]);
if(!isdigit(str[i])){
printf(" ");
}
}
if(str[i]=='+' || str[i]=='-'){
if(!StackLength(S)){
Push(S,str[i]);
} else{
do{
Pop(S,&X);
if(X=='('){
Push(S,X);
} else{
printf("%c ",X);
}
} while (StackLength(S) && X!='(');
Push(S,str[i]);
}
} else if(str[i]==')'){
Pop(S,&X);
while (X!='('){
printf("%c ",X);
Pop(S,&X);
}
} else if(str[i]=='*' || str[i]=='/' || str[i]=='('){
Push(S,str[i]);
} else if(str[i]=='\0'){
break;
} else{
printf("输入有误\n");
return;
}
i++;
}
while (StackLength(S)){
Pop(S,&X);
printf("%c ",X);
}
}
测试:
int main(void){
ElementType str[30];
Stack S=(Stack) malloc(sizeof (struct SNode));
gets(str);
Change(S,str);
return 0;
}
注意:Stack S=(Stack) malloc(sizeof (struct SNode));这里必须要这样写,而不是Stack S;因为我们在定义堆栈的时候我们在改写别名的时候typedef struct SNode *Stack;定义的是结构体指针Stack。
关于结构体指针变量和结构体变量的不同这里提一下。结构体变量定义的就直接是结构体,而结构体指针变量定义的是指针,必须把它和结构体联系起来才能当作结构体用,方式就是用malloc申请空间或者赋值其他对象地址,让其指向结构体。