一.问题描述
简单算术表达式:只包含“+”、“-”、“*”、“/”、正整数和括号的算术表达式并且是合法数学表达式。
二.后缀表示法(RPN)
参考资料:《大话数据结构》 p104
表达式计算,即符合“先乘除,后加减,从左到右,先括号内后括号外”这个运算规则就行。算法方式有很多,此处只谈RPN(Reverse Polish Notation)。
算法核心:
(1)将中缀表达式转化为后缀表达式(栈用来进出运算的符号)
规则:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或优先级低于栈顶符号(乘除优先加减)则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止。
(2)将后缀表达式进行运算得出结果(栈用来进出运算的数字)
规则:从左到右遍历表达式的每个数字和符号,遇到的是数字就进栈,遇到的是符号,就将处于栈顶两个数字出栈,进行运算,运算结果进栈,一直到最终获得结果。
三.实现
/*
* RPN.c
* Created on: 2015年5月5日
* <span style="white-space:pre"> </span>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;
}