中缀表达式转后缀表达式并求值

因为在学校实在是太闲了,所以写了一个表达式求值的C语言程序,希望大佬可以多多指正。

基本思路:

就像把大象装进冰箱一样,我们需要三步进行表达式的求值工作。

  • 输入一个中缀表达式(就是平常我们见的表达式)
  • 将中缀表达式转换为后缀表达式
  • 根据后缀表达式求值。

第一步:

输入一个中缀表达式比较简单,不过为了编写代码更加简单,这里对输入的表达式进行一些规范

  • 所有的输入字符与数字之间,采取空格进行分开
  • 不可以对乘号进行省略
  • 数字后面要跟一个空格再输入操作符。

第二步:

中缀表达式转后缀表达式的基本过程为:从头到尾读取中缀表达式的每个对象,对不同的对象按照不同的情况处理,分为以下六种情况:

  • 如果是空格,认为是分割符,不处理
  • 如果是运算数,直接输出
  • 如果是左括号,则将其压入堆栈中
  • 如果是右括号,表明括号内的中缀表达式已经扫描完毕,将栈顶的运算符弹出并输出,直到遇到左括号(左括号不输出)
  • 如果遇到运算符,如果现在栈空,则直接将运算符放入栈中,若不空,则将此运算符与栈顶的运算符进行优先级比较,如果该运算符的优先级小于等于栈顶运算符的话,则将栈顶运算符输出,并再次将此运算符与新的栈顶运算符进行比较,直到该运算符的优先级大于栈顶运算符的优先级为止,然后将该运算符压栈
  • 若中缀表达式中的各个元素处理完毕,则把堆栈中存留的运算符一并输出。

执行完所有的流程,我们就输出了一个中缀表达式。


当然了,在实际实现的时候,我们对格式依然有所要求。后缀表达式不同于中缀表达式,后缀表达式中可能会出现好多个数字连在一起的情况,所以,对连续输出的数字,我们要给中间加上空格进行分割,以此区分是一个两位的十进制数,还是两个一位的十进制数。

第三步:

根据后缀表达式求值,我想大家学习了栈的知识,并弄明白后缀表达式是什么的话,就可以非常容易理解如何使用栈对后缀表达式进行求值,详情请看我的另一篇博客 点我看栈的实现以及后缀表达式求值.


三步都弄懂了,如何将这三步联合在一起呢?

在介绍之前,先吐槽一下C语言,连个能直接调的栈都没有,如果我要调用两个不一样的栈,一个装int,一个装char,那么我还需要写两套定义,否则在一个程序中没办法实现。(可能可以实现,但我不会,我很抱歉)

联合

首先我们将第二步和第三步的内容进行封装,自定义两个头文件,并对内容进行实现,在定义的过程中,我发现有一些定义是重复的,所以我们将重复定义的东西放到另外一个头文件中。

文件名作用
same.h定义一些重复的宏
TransformationToInfixExpression.h定义转换为后缀表达式的头文件
ExpressionEvaluation.h定义后缀表达式求值的头文件
TransformationToInfixExpression.c实现前面同名的头文件
ExpressionEvaluation.c实现前面同名的头文件
test.c主函数,用于实现第一步

same.h

#define MAXOP 100			/*操作数序列可能的最大长度*/
#define INFINITY 1e9 		/*代表正无穷*/
#define ERROR -INFINITY
typedef int Position;
typedef char ElementType1;
typedef double ElementType2;
struct SNode1{
	ElementType1 *Data;		/*存储元素的数组*/
	Position Top;			/*栈顶指针*/	
	int MaxSize;			/*堆栈最大容量*/
};
struct SNode2{
	ElementType2 *Data;		/*存储元素的数组*/
	Position Top;			/*栈顶指针*/	
	int MaxSize;			/*堆栈最大容量*/
};

TransformationToInfixExpression.h

//TransformationToInfixExpression.h
#ifndef _TRANSFORMATIONTOINFIXEXPRESSION_H_ 
#define _TRANSFORMATIONTOINFIXEXPRESSION_H_
	//结构体和函数声明....
	typedef struct SNode1 *PtrToSNode1;
	typedef PtrToSNode1 Stack1;
	typedef char ElementType1;
	//1. 将一个中缀表达式转换为后缀表达式
	void TransformationToInfixExpression(char* Expr, char* Aim);
	//判断字符的优先级
	int Priority(char a,char b);
	
	Stack1 CreateStack1(int MaxSize);

	int IsFull1(Stack1 S);
	
	int Push1(Stack1 S,ElementType1 X);

	int IsEmpty1(Stack1 S);
	
	ElementType1 Pop1(Stack1 S);
	
	ElementType1 Peek1(Stack1 S);
	
#endif

ExpressionEvaluation.h

//ExpressionEvaluation.h
//条件编译
#ifndef _EXPRESSIONEVALUATION_H_ 
#define _EXPRESSIONEVALUATION_H_ 
	//结构体和函数声明....
	typedef enum {true,false} bool;
	typedef enum {num, opr, end} Type;
	typedef struct SNode2 *PtrToSNode2;
	typedef PtrToSNode2 Stack2;
	typedef double ElementType2;
	Stack2 CreateStack2(int MaxSize);

	bool IsFull2(Stack2 S);

	bool Push2(Stack2 S,ElementType2 X);

	bool IsEmpty2(Stack2 S);

	ElementType2 Pop2(Stack2 S);
	
	ElementType2 Peek2(Stack2 S);
	
	Type GetOp(char * Expr, int * start,char * str);
	
	ElementType2 PostfixExp(char* Expr);

#endif

TransformationToInfixExpression.c

#include"TransformationToInfixExpression.h"
#include"same.h"
#include"ExpressionEvaluation.h"
#include<string.h>
#include<ctype.h>
#include<stdio.h>
#include<stdlib.h>
Stack1 CreateStack1(int MaxSize){
	Stack1 S = (Stack1)malloc(sizeof(Stack1));
	S->Data = (ElementType1*)malloc(MaxSize*sizeof(ElementType1));
	S->Top = -1;
	S->MaxSize = MaxSize;
	return S;
}
int IsFull1(Stack1 S){
	return (S->Top == S->MaxSize - 1);
}
int Push1(Stack1 S,ElementType1 X){
	if(IsFull1(S)){
		printf("堆栈满\n");
		return 0;
	}else{
		S->Data[++(S->Top)] = X;
		return 1;
	}
}
int IsEmpty1(Stack1 S){
	return (S->Top == -1);
}
ElementType1 Pop1(Stack1 S){
	if(IsEmpty1(S)){
		printf("堆栈空1\n");
		return (ElementType1)ERROR;
	}else{
		return S->Data[(S->Top)--];
	}
}
ElementType1 Peek1(Stack1 S){
	if(IsEmpty1(S)){
		printf("堆栈空2\n");
		return (ElementType1)ERROR;
	}else{
		return S->Data[(S->Top)];
	}
}


void TransformationToInfixExpression(char* Expr, char* Aim){
	int n = strlen(Expr);
	Stack1 S = CreateStack1(MAXOP);
	int j = 0;
	for (int i = 0; i < n; i++){
		if (Expr[i] == ' '){
			if(j != 0 && Aim[j-1] != ' '){
				Aim[j++] = Expr[i];
			}
			continue;
		} else if(isdigit(Expr[i]) || Expr[i] == '.'){
			Aim[j++] = Expr[i];
		} else if(Expr[i] == '('){
			Push1(S,Expr[i]);
		} else if(Expr[i] == ')'){
			char c = Pop1(S);
			while(c != '('){
				Aim[j++] = c;
				Aim[j++] = ' ';
				c = Pop1(S);
			}
		} else {
			if(!IsEmpty1(S)){
				char c = Peek1(S);
				while(Priority(Expr[i],c) <= 1){
					Aim[j++] = Pop1(S);
					Aim[j++] = ' ';
					if(!IsEmpty1(S)){
						c = Peek1(S);
					}else{
						break;
					}
				}
			}
			Push1(S,Expr[i]);
		}
	}
	Aim[j++] = ' ';
	while(!IsEmpty1(S)){
		Aim[j++] = Pop1(S);
		Aim[j++] = ' ';
	}
}

int Priority(char a,char b){
	/*
		a==b  0;
		a<b   1;
		a>b   2;
	*/
	if(a == '*' || a == '/'){
		if(b == '*' || b == '/'){
			return 0;
		}else{
			return 2;
		}
	}else if(a == '+' || a == '-'){
		if(b == '*' || b == '/'){
			return 1;
		}else if(b == '+' || b == '-'){
			return 0;
		}else {
			return 2;
		}
	}
}

ExpressionEvaluation.c

//这个程序是利用堆栈求后缀表达式的值
#include"ExpressionEvaluation.h"
#include"same.h"
#include"TransformationToInfixExpression.h"
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<string.h>
Stack2 CreateStack2(int MaxSize){
	Stack2 S = (Stack2)malloc(sizeof(Stack2));
	S->Data = (ElementType2*)malloc(MaxSize*sizeof(ElementType2));
	S->Top = -1;
	S->MaxSize = MaxSize;
	return S;
}
bool IsFull2(Stack2 S){
	return (S->Top == S->MaxSize - 1);
}
bool Push2(Stack2 S,ElementType2 X){
	if(IsFull2(S)){
		printf("堆栈满\n");
		return false;
	}else{
		S->Data[++(S->Top)] = X;
		return true;
	}
}
bool IsEmpty2(Stack2 S){
	return (S->Top == -1);
}
ElementType2 Pop2(Stack2 S){
	if(IsEmpty2(S)){
		printf("堆栈空\n");
		return (ElementType2)ERROR;
	}else{
		return S->Data[(S->Top)--];
	}
}
ElementType2 Peek2(Stack2 S){
	if(IsEmpty2(S)){
		printf("堆栈空2\n");
		return (ElementType2)ERROR;
	}else{
		return S->Data[(S->Top)];
	}
}

Type GetOp(char * Expr, int * start,char * str){
	/*从*start开始读下一个对象(操作数或者操作符),并保存在字符串str中*/
	
	int i = 0;
	/*跳过表达式前的空格*/
	while((str[0] = Expr[(*start)++])==' ');
	
	while(str[i]!=' ' && str[i] != '\0')
		str[++i] = Expr[(*start)++];
	if(str[i] == '\0')			/*如果读到输入的结尾*/
		(*start)--;				/*start指向结束符*/
	str[i] = '\0';				/*结束一个对象的获取*/
	
	if(i == 0) return end;		/*读到了结束*/
		/*如果str[0]是数字,或者是符号跟个数字*/
	else if(isdigit(str[0]) || isdigit(str[1]))
		return num;	/*表示str中存的仅仅是一个数字*/
	else
		return opr;	/*表示str中存的是一个表达式*/
}
ElementType2 PostfixExp(char* Expr){
	Stack2 S;
	Type T;
	ElementType2 Op1,Op2;
	char str[MAXOP];
	int start = 0;
	
	/*申请一个新的堆栈*/
	S = CreateStack2(MAXOP);
	
	Op1 = Op2 = 0;
	while((T = GetOp(Expr,&start,str))!=end){
		/*当未读到结束时*/
		
		if(T == num){
			Push2(S,atof(str));
		} else {
			if(!IsEmpty2(S)) Op2 = Pop2(S);
			else Op2 = INFINITY;
			
			if(!IsEmpty2(S)) Op1 = Pop2(S);
			else Op2 = INFINITY;
			switch(str[0]){
				case'+':Push2(S,Op1 + Op2);break;
				case'*':Push2(S,Op1 * Op2);break;
				case'-':Push2(S,Op1 - Op2);break;
				case'/':
				if(Op2 != 0.0)/*检查除法的分母是不是等于0*/
					Push2(S,Op1 / Op2);
				else{
					printf("错误:除法分母为0\n");
					Op2 = INFINITY;
				}
				break;
				default:
					printf("错误:未知运算符:%s\n",str);
					Op2 = INFINITY;
					break;
			}
			if(Op2 == INFINITY) break;
		}
	}
	if(Op2 <INFINITY)/*处理完了表达式*/
		if(!IsEmpty2(S))/*而此时堆栈正常*/
			Op2 = Pop2(S);
		else Op2 = INFINITY;	/*否则标记错误*/
	free(S); /*释放堆栈*/
	return Op2;
}

test.c

#include"ExpressionEvaluation.h"
#include"TransformationToInfixExpression.h"
#include"same.h"
#include<stdio.h>
int main(){
	printf("请输入中缀表达式:\n");
	char Expr[MAXOP] = {0};
	char Aim[MAXOP] = {0};
	gets(Expr);
	TransformationToInfixExpression(Expr,Aim);
	printf("转换的后缀表达式为:%s\n",Aim);
	double r =  PostfixExp(Aim);
	printf("计算的结果为:%.4lf\n",r);
}

接下来就大功告成了,对这几个C文件进行联调,即可运行最后的程序,我们看一下效果吧 ^ - ^

效果:

编译命令是第一行:这里报错是因为gets已经不安全了,这里建议使用fgets,这里就不管了,不影响我们正常使用。

在这里插入图片描述
执行test文件:
在这里插入图片描述
到这里我们就可以输入一个我们看得懂的表达式,然后直接输出结果了。

觉得我写的还不错的话,希望可以点个赞哦,如果有更好的方案的话,也欢迎多多指教哦。


  • 思路和部分代码参考《数据结构(第二版)》 — 高等教育出版社
  • 6
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值