中缀表达式求值并转换逆波兰表达式

算术表达式的三种表示方式

  1. 中缀表达式:运算符位于与其相关的操作数中间,如(1+2)*(3-4)
  2. 前缀表达式(波兰式):运算符位于与其相关的操作数前面,如* + 1 2 - 3 4
  3. 后缀表达式(逆波兰式):运算符位于与其相关的操作数后面,如1 2 + 3 4 - *

表达式求值思路

基本思路:栈+线性扫描(当前操作符比栈顶的操作符优先级低,则进行一次实际的运算)

中缀表达式求值的C代码实现

思路参考邓俊辉的《数据结构C++版本》的第4章-栈与队列以及《C程序设计语言》的4.3节-外部变量((另一种思路是先将中缀表达式转换成后缀表达式,再对后缀表达式求值,具体分析可参考 详解如何将中缀表达式转化为后缀表达式

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define MAXSIZE 100
#define OPNUMLEN 20
#define NUMBER '0'  //操作数的标志
#define BUFSIZE 100

char buf[BUFSIZE];
int bufp = 0;
double stack[MAXSIZE]; //操作数栈
int sp = 0; //栈顶索引
char optrOrderMat[][8] = {">><<><>", 
						  ">><<><>", 
						  ">>>>><>", 
						  ">>>>><>", 
						  "<<<<=<=",
						  "<<<<=<=", 
						  "======="}; //操作符优先级表,顺序为+-*/'\0'()
char stackC[MAXSIZE]; //操作符栈
int spC = 0;


char getch() //从缓冲区中读取一个字符
{
	return (bufp>0) ? buf[--bufp]:getchar();
}

void ungetch(char c) //将字符退回到缓冲区
{
	if(bufp > BUFSIZE)
		printf("ungetch: exceed buffer size");
	buf[bufp++] = c;
}

int getop(char s[]); //获取一个操作对象,s保存操作数串,返回操作符
void push(double n);	//栈压入
double pop(void);	//栈弹出
int optr2idx(char optr); //将运算符转换成下标索引
int orderBetween(char optr1, char optr2); //比较两个运算符的优先级(限于四则运算)
double calcMidExpAndGotRPM(char RPM[]); //中缀表达式求值并将其转换成逆波兰表达式

int main()
{
	//在windows的powershell中测试通过
	char rpm[MAXSIZE]={'\0'};
	double res = calcMidExpAndGotRPM(rpm);
	printf("%f\n%s\n", res, rpm);
	return 0;
}

//获取一个操作对象,s保存操作数串,返回操作符
int getop(char s[])
{
	int i, c;
	while((s[0]=c=getch()) == ' ' || c=='\t') //去除前导空白符
	;
	if(!isdigit(c) && c!='.')  //.xx算一个操作数
		return c; // 不是一个数,直接返回
	i = 0;
	if(isdigit(c)) //收集整数部分
		while(isdigit(s[++i]=c=getch()))  //跳出循环时s[i]保存了第一个非数字字符
		;
	if(c == '.') //收集小数部分
		while(isdigit(s[++i]=c=getch()))
		;
	s[i] = '\0';
	if(c != EOF)
		ungetch(c); //将多读入的一个字符放回缓冲区
	return NUMBER;
}

void push(double n)
{
	if(sp < MAXSIZE)
		stack[sp++] = n;
	else
		printf("error:stack full, can't push %g\n", n);
}

double pop()
{
	if(sp > 0)
		return stack[--sp]; //只是将栈顶指针前移了,在stack中的元素并未被删除
	else
		{
			printf("error:stack empty!\n");
			return 0.0;
		}
}

void pushC(char n)
{
	if(spC < MAXSIZE)
		stackC[spC++] = n;
	else
		printf("error:stack full, can't push %g\n", n);
}

char popC()
{
	if(spC > 0)
		{
			char c =  stackC[--spC];
//			stackC[spC] = '\0';
			return c;
		}
	else
		{
			printf("error:stack empty!\n");
			return 0.0;
		}
}


int optr2idx(char optr)
{
	int i;
	switch(optr){
		case '+':
			i = 0;
			break;
		case '-':
			i = 1;
			break;
		case '*':
			i = 2;
			break;
		case '/':
			i = 3;
			break;
		case '\0':
			i = 4;
			break;
		case '(':
			i = 5;
			break;
		case ')':
			i = 6;
			break;
		default:
			i = -1;
			break;
	}
	return i;
}

//比较两个运算符的优先级(限于四则运算)
int orderBetween(char optr1, char optr2)
{
	int idx1,idx2;
	if((idx1=optr2idx(optr1))!=-1 && (idx2=optr2idx(optr2))!=-1)
		return optrOrderMat[idx1][idx2];
	printf("unsupported operator\n");
	return -1;
}

//中缀表达式求值并将其转换成逆波兰表达式
double calcMidExpAndGotRPM(char RPM[])
{
	double optn[MAXSIZE];
	char c, op, optr[MAXSIZE];
	pushC('\0'); //字符串结束符先入操作符栈
	int j, i=0;
	char str[OPNUMLEN];
	while(spC>0)
	{
		c = getop(str);
		if(c == NUMBER)
		{
			push(atof(str)); //将操作数转换为浮点数并入栈
			strcat(str, " ");
			strcat(RPM, str);
		}
		else
		{
			if(c == '\n') //一行字符串表达式结束时,用'\0'替换末尾的'\n'
				c = '\0';
			if(c == EOF) //windows下按Ctrl+Z相当于EOF
				break;
			switch(orderBetween(stackC[spC-1],c)){
			case '<':	//栈顶运算符优先级更低
				pushC(c);
				break;
			case '=':	//优先级相同(当前运算符为右括号或'\0',且栈顶为左括号或'\0')
				popC();
				break;
			case '>':	//栈顶运算符优先级更高,实施相应运算,结果入栈
				op = popC();
				j = strlen(RPM);
				RPM[j++] = op;
				RPM[j] = '\0';
				strcat(RPM, " ");
				double opnd2;
				switch(op){
					case '+':
						push(pop()+pop());
						break;
					case '-':
						opnd2 = pop(); //不能写成(pop()-pop()),因为C并没有规定'-'两边的求值顺序
						push(pop()-opnd2);
						break;
					case '*':
						push(pop()*pop());
						break;
					case '/':
						opnd2 = pop();
						if(opnd2 != 0.0)
							push(pop()/opnd2);
						else
							printf("error:zero divisor\n");
						break;
				}
				ungetch(c); // 执行完运算符操作后需把当前读入的操作符“反读”回去
			}
		}
		for(i=1; i<spC; ++i)
			printf("%c ", stackC[i]);
		printf("\t%s\n", RPM);
	}
	return pop(); // 弹出最后的求值结果
}

测试结果

在这里插入图片描述

发布了92 篇原创文章 · 获赞 127 · 访问量 23万+

猜你喜欢

转载自blog.csdn.net/Blateyang/article/details/100641711