【Algorithm】算术表达式求值------逆波兰RPN

一.问题描述

简单算术表达式:只包含“+”、“-”、“*”、“/”、正整数和括号的算术表达式并且是合法数学表达式。

二.后缀表示法(RPN)

参考资料:《大话数据结构》 p104

表达式计算,即符合“先乘除,后加减,从左到右,先括号内后括号外”这个运算规则就行。算法方式有很多,此处只谈RPN(Reverse Polish Notation)

算法核心:

(1)将中缀表达式转化为后缀表达式(栈用来进出运算的符号)

规则:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或优先级低于栈顶符号(乘除优先加减)则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止。

(2)将后缀表达式进行运算得出结果(栈用来进出运算的数字)

规则:从左到右遍历表达式的每个数字和符号,遇到的是数字就进栈,遇到的是符号,就将处于栈顶两个数字出栈,进行运算,运算结果进栈,一直到最终获得结果。

三.实现

/*
 * 	RPN.c
 * 	Created on: 2015年5月5日
 *  	Author: weike
 *	字符串表达式,采用逆波兰实现
 */
#include "stdio.h";
#include "stdlib.h";

#define SIZE 20

typedef struct { //栈用来保存运算符
	int top;
	char data[SIZE];
} stack_operator;
typedef struct { //栈用来保存数字
	int top;
	double data[SIZE];
} stack_nums;

stack_operator op;
stack_nums st;

//中缀表达式转换为后缀表达式
void exchange(char exps[], char postexpss[]) {
	//初化栈
	op.top = -1;
	char ch; //接收字符串exps
	int i = 0, j = 0;
	ch = exps[i]; //获取exps字符串的第一个字符
	i++;
	/**
	 *	1.若ch为数字,将后继的所有数字均一次存放到postexpss[]中,并以字符"#"标志结束
	 *	2.若ch为"(",则将此括号进栈
	 *	3.若ch为")",则将运算符栈中"("以前的运算符依次出栈并存放到postexpss[]中,将"("删除
	 *	4.若ch的运算符优先级不大于运算符栈的栈顶运算符(除栈顶运算符为"("外)的优先级,
	 *	      则依次出栈并存入到postexpss[]中,然后将ch进栈
	 *	5.若字符串exps[]扫描完毕,则将运算符栈中的所有运算符依次出栈并存放到postexpss[]中
	 */
	while (ch != '\0') {
		switch (ch) {
		case '(':
			op.top++;
			op.data[op.top] = ch;
			break;
		case ')':
			while (op.data[op.top] != '(') {
				postexpss[j] = op.data[op.top];
				j++;
				op.top--;
			}
			op.top--;
			break;
		case '+':
		case '-':
			while (op.top != -1 && op.data[op.top] != '(') {
				postexpss[j] = op.data[op.top];
				j++;
				op.top--;
			}
			op.top++;
			op.data[op.top] = ch;
			break;
		case '*':
		case '/':
			while (op.top != -1 && op.data[op.top] != '('
					&& (op.data[op.top] == '*' || op.data[op.top] == '/')) {
				postexpss[j] = op.data[op.top];
				j++;
				op.top--;
			}
			op.top++;
			op.data[op.top] = ch;
			break;
		default:
			while (ch >= '0' && ch <= '9') {//ch为数字,输出,作为后缀表达式的一部分
				postexpss[j] = ch;
				j++;
				ch = exps[i];
				i++;
			}
			i--;
			postexpss[j] = '#';//用#标识一个数值串结束
			j++;
		}
		ch = exps[i];
		i++;
	}

	while (op.top != -1) {
		postexpss[j] = op.data[op.top];
		j++;
		op.top--;
	}
	postexpss[j] = '\0';
}
//计算后缀表达式
double calculate(char postexp[])
{
	double d;
	char ch;
	int i = 0;
	st.top = -1;
	ch = postexp[i];
	i++;
	/**
	 *	1.若ch为数字,将后继的所有数字构成一个整数存放到数值栈st中
	 *	2.若ch为 +,则从数值栈st中出栈两个运算数,相加后进栈st中
	 *	3.若ch为 -,则从数值栈st中出栈两个运算数,相减后进栈st中
	 *	4.若ch为 *,则从数值栈st中出栈两个运算数,相乘后进栈st中
	 *	5.若ch为 /,则从数值栈st中出栈两个运算数,相除后进栈st中(若除数为0,则提示错误)
	 */
	while(ch != '\0')
	{
		switch(ch)
		{
			case '+':
				st.data[st.top-1] = st.data[st.top-1] + st.data[st.top];
				st.top--;
				break;
			case '-':
				st.data[st.top-1] = st.data[st.top-1] - st.data[st.top];
				st.top--;
				break;
			case '*':
				st.data[st.top-1] = st.data[st.top-1] * st.data[st.top];
				st.top--;
				break;
			case '/':
				if(st.data[st.top] != 0)
					st.data[st.top-1] = st.data[st.top-1] / st.data[st.top];
				else
				{
					printf("\n\t除数不能为0\n");
					exit(0);
				}
				st.top--;
				break;
			default:
				d = 0;
				while(ch>='0' && ch<='9')
				{
					d = 10*d +ch -'0';//转换为数值

					ch = postexp[i];
					i++;
				}
				st.top++;
				st.data[st.top] = d;
		}
		ch = postexp[i];
		i++;
	}
	return st.data[st.top];
}
int main(int argc, char **argv) {
	char exps[] = "9+(3-1)*3+10/2";
	char postexpss[30];
	exchange(exps, postexpss);
	double result;
	result = calculate(postexpss);
	printf("%s = %f\n", postexpss, result);
	return 0;
}



你可能感兴趣的:(C,Algorithm)