一、基本概念
中缀表达式
操作符在运算数之间(如:1 + 1);
后缀表达
操作符以后缀形式位于两个运算数后(如:1 1 +)。
中缀表达式常用括号来将操作数与相应的操作符括起来,用此来指示运算的次序。
二、中缀表达式转为后缀表达式规则
从左往右一次遍历中缀表达式的各个字符
(需要一个操作栈来存放操作符与括号;还要一个运算数栈来存放运算数)
1、字符为 运算数 :
直接送入后缀表达式
2、字符为 左括号 :
直接入栈,优先级降维最低
3、字符为 右括号 :
直接出栈,并将出栈字符依次送入后缀表达式,直到栈顶元素为左括号(左括号也要出栈)
4、字符为 操作符 :
若栈为空栈,则直接入栈;
若栈不为空,判断栈顶操作符,若栈顶操作符优先级低于该操作符,该操作符入栈;否则一直出栈,并将出栈字符依次送入后缀表达式,直到栈空或栈顶操作符优先级低于该操作符,该操作符再入栈。
总结:只要满足栈空 / 优先级高于栈顶操作符,停止出栈,直接入栈
5、中缀表达式遍历完成,栈中可能还有字符未输出,故需要判断栈空。
三、后缀表达式的计算
从左往右依次遍历后缀表达式
1、字符为 运算数:
直接入栈
2、字符为 操作符:
连续出栈两次,并进行相应的计算,将计算结果入栈
四、算法实现
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<string.h>
#include<ctype.h>
#define ERROR 0
#define OK 1
#define STACK_INT_SIZE 10 /*存储空间初始分配量*/
#define STACKINCREMENT 5 /*存储空间分配增量*/
#define M 50
typedef char ElemType; /*定义字符数据类型*/
typedef double ElemType2; /*定义运算数数据类型*/
/*字符栈*/
typedef struct {
ElemType* base;
ElemType* top;
int stacksize;
}SqStack;
/*运算数栈*/
typedef struct {
ElemType2* base;
ElemType2* top;
int stacksize;
}NStack;
int InitStack(SqStack* S); /*构造空栈*/
int Push(SqStack* S, ElemType e); /*入栈*/
int Pop(SqStack* S, ElemType* e); /*出栈*/
int StackEmpty(SqStack* s); /*栈空判断*/
void in2post(ElemType* str, ElemType* p); /*中缀表达式转后缀表达式*/
double cal_post(char* str); /*计算后缀表达式*/
/*字符栈初始化*/
int InitStack(SqStack* S)
{
S->base = (ElemType*)malloc(STACK_INT_SIZE * sizeof(ElemType));
if (!S->base)
return ERROR; //分配失败
S->top = S->base;
S->stacksize = STACK_INT_SIZE;
return OK;
}/*InitStack*/
/*初始化运算数栈*/
int InitStack_N(NStack* S)
{
S->base = (ElemType2*)malloc(STACK_INT_SIZE * sizeof(ElemType2));
if (!S->base)
return ERROR; //分配失败
S->top = S->base;
S->stacksize = STACK_INT_SIZE;
return OK;
}
/*字符栈入栈*/
int Push(SqStack* S, ElemType e)
{
//判断栈满
if (S->top - S->base >= S->stacksize)
{
S->base = (ElemType*)realloc(S->base, (S->stacksize + STACKINCREMENT) * sizeof(ElemType));
if (NULL == S->base) //分配失败
return ERROR;
S->top = S->base + S->stacksize; //?是什么相加
S->stacksize = S->stacksize + STACKINCREMENT;
}
*S->top = e;
S->top++;
return OK;
}
/*运算数栈入栈*/
int Push_N(NStack* S, ElemType2 e)
{
if (S->top - S->base >= S->stacksize)
{
S->base = (ElemType2*)realloc(S->base, (S->stacksize + STACKINCREMENT) * sizeof(ElemType2));
if (NULL == S->base)
return ERROR;
S->top = S->base + S->stacksize;
S->stacksize = S->stacksize + STACKINCREMENT;
}
*S->top = e;
S->top++;
return OK;
}
/*字符栈出栈*/
int Pop(SqStack* S, ElemType* e)
{
//判断栈空
if (S->top == S->base)
return ERROR;
S->top--;
*e = *S->top;
return OK;
}/*Pop*/
/*运算数栈出栈*/
int Pop_N(NStack* S, ElemType2* e)
{
if (S->top == S->base)
return ERROR;
S->top--;
*e = *S->top;
return OK;
}
/*判断栈空*/
int StackEmpty(SqStack* s)
{
if (s->top == s->base)
return OK;
return ERROR;
}/*StackEmpty*/
//str为待转换的中缀表达式字符串,p为转换后的后缀表达式字符串
void in2post(ElemType* str, ElemType* p) { /*infix to postfix*/
SqStack s;
InitStack(&s); //初始化一个空字符栈
ElemType e;
int i;
int j = 0;
for (i = 0; i < strlen(str); i++) //遍历中缀表达式
{
//遇到数字和小数点直接输出
//使用循环完整接收一个运算数
while (isdigit(str[i]) || '.' == str[i])
{
p[j++] = str[i++];
if (!isdigit(str[i]) && '.' != str[i])
p[j++] = ' '; //一个数字完整输出后使用空格与其它运算符或数字分隔开
}
//遇到左括号直接入栈
if ('(' == str[i])
Push(&s, str[i]);
//遇到右括号直接出栈,直到栈顶为左括号
if (')' == str[i])
{
while ('(' != *(s.top - 1))
{
Pop(&s, &e);
p[j++] = e;
p[j++] = ' ';
}
Pop(&s, &e); //左括号出栈但不输出
}
//遇到+或—
//1.栈空/栈顶为左括号:直接入栈
//2.否则一直出栈,直到栈空/栈顶为左括号,再入栈
if ('+' == str[i] || '-' == str[i])
{
while (!StackEmpty(&s) && '(' != *(s.top - 1))
{
Pop(&s, &e);
p[j++] = e;
p[j++] = ' ';
}
Push(&s, str[i]);
}
//遇到*或/
//1.栈空/栈顶为左括号/栈顶操作符为+ or -:直接入栈
//2.否则一直出栈,直到满足1,再入栈
if ('*' == str[i] || '/' == str[i] || '%' == str[i])
{
while (!StackEmpty(&s) && '(' != *(s.top - 1) && '+' != *(s.top - 1) && '-' != *(s.top - 1))
{
Pop(&s, &e);
p[j++] = e;
p[j++] = ' ';
}
Push(&s, str[i]);
}
}
//中缀表达式遍历完成,还需检查栈中是否有未输出字符
//判断栈空,非空则直接出栈并输出(左括号不用输出)
while (!StackEmpty(&s)) {
Pop(&s, &e);
if ('(' != e)
{
p[j++] = e;
p[j++] = ' ';
}
}
p[--j] = '\0';
}/*infix2postfix*/
//str为待计算的后缀表达式,返回值为计算结果
double cal_post(char* str) /*计算后缀表达式*/
{
int i;
ElemType2 e, a, b;
char d[M];
NStack n;
InitStack_N(&n); //初始化一个运算数栈保存运算数
for (i = 0; i < strlen(str); i++)
{
int j = 0;
while (isdigit(str[i]) || '.' == str[i])
{
d[j++] = str[i++];
d[j] = '\0';
if (!isdigit(str[i]) && '.' != str[i])
{
e = atof(d); //使用atof()将字符串形式的运算数转换为double型数据
Push_N(&n, e); //运算数入栈
}
}
switch (str[i])
{
case '+':
Pop_N(&n, &b);
Pop_N(&n, &a);
Push_N(&n, a + b);
break;
case '-':
Pop_N(&n, &b);
Pop_N(&n, &a);
Push_N(&n, a - b);
break;
case '*':
Pop_N(&n, &b);
Pop_N(&n, &a);
Push_N(&n, a * b);
break;
case '/':
Pop_N(&n, &b);
Pop_N(&n, &a);
Push_N(&n, a / b);
break;
}
}
Pop_N(&n, &e);
return e;
}/*calculate_postfix*/
int main()
{
char str[M];
char post[M];
int i;
printf("\n输入一串中缀表达式:\n");
gets(str);
printf("\n对应的后缀表达式:\n");
in2post(str, post);
printf("%s", post);
printf("\n\n计算后缀表达式:\n");
printf("%f", cal_post(post));
return 0;
}
五、算法改进
1、判断中缀表达式是否合法
2、运算数为非负数