表达式求值,属于数据结构——栈的典型应用。使用后缀表达式的与原因,是因为在求值的过程中,不需要考虑操作符的优先级。(结合性仍需要考虑)
但是一般的书上只讲到如何处理二元操作符,并且结合性都是从左到右结合的。这里的实现能够处理一元操作符,并且能够处理从右向左结合的幂乘'^'操作符。
功能需求
给定一个中缀表达式,求出该表达式的值。
要求支持“加减乘除 + - * /、取余 %、幂运算 ^、一元正负号 + -、括号 ()、数的科学表示法符号 E e”,其中含有一元操作符'+ -',也含有从右向左结合性的操作符'^',注意操作数的位数可以为多位。
分析
需要考虑三个方面:① 优先级; ② 结合性; ③ 几元操作符;
优先级:若操作符的优先级不同,那么先计算优先级高的。
结合性:若操作符的优先级相同,那么先计算优先级的结合性决定求值顺序。
几元操作符:一元和二元操作符,决定了参与求值的操作数的个数。
处理方法
1. 利用数字0,将一元操作转化为二元操作,例如将"-3"转化为"0-3";
2. 将操作符按照优先级分类,确定各个操作符的优先级;
3. 结合性不同的操作符,由于数量较少(这里只有'^'),处理时单独判断;
具体步骤
1. 判断是否有不匹配的括号
2. 预处理中缀表达式
2.1 判断是否有不能处理的字符;
2.2 去除所有的空格;
2.3 处理一元操作符'+'和'-':
2.3.1 如果是在开始位置,在开始处添加"0";
2.3.2 如果是在“非数字字符”(右括号‘)’除外)之后,那么先在一元操作符前插入"(0",然后在一个“完整的数字”或者“括号后面”添加右括号")";
3. 中缀转化为后缀表达式 (利用“操作符栈”)
遍历中缀表达式:
3.1 如果是操作数,读取其所有的位,然后进入后缀表达式队列;
3.2 如果是操作符( + – * / ^ % )
3.2.1 如果“操作符栈”为空,直接入栈;
3.2.2 如果当前操作符优先级>栈顶元素优先级,那么入栈;
3.2.3 如果当前操作符优先级<栈顶元素优先级,那么栈顶操作符出栈,循环执行;
3.2.4 如果当前操作符优先级=栈顶元素优先级,如果当前元素是右结合性,那么入栈;否则,栈顶元素出栈;循环执行。
3.3 如果是左括号'(',直接入栈
3.4 如果是右括号')',如果栈非空,那么栈顶元素出栈,直到遇到左括号'(';
3.5 遍历结束中,将操作符栈中的元素依次出栈,添加到后缀表达式队列中。
4. 计算后缀表达式
从后缀表达式队列中依次取出元素
4.1 如果是操作数,那么将其压入“结果栈”中;
4.2 如果是操作符,从“结果栈”中取出两个元素,进行计算。(注意从栈中取元素的顺序和操作数的顺序是相反的)
遍历后缀表达式结束后,“结果栈”中的元素就是最终的结果。
具体代码
/**
* 中缀表达式-->后缀表达式-->表达式求值
*
*/
#include <cstdlib>
#include <iostream>
#include <string>
#include <cctype>
#include <set>
#include <queue>
#include <stack>
#include <map>
#include <cmath>
using namespace std;
/**
* 判断是否为操作符
* 参数:
* ch : 待判断的字符
* 返回值:
* 是操作符,返回true;否则返回false;
*/
bool is_operator(char ch) {
set<char> operator_set;
operator_set.insert('+');
operator_set.insert('-');
operator_set.insert('*');
operator_set.insert('/');
operator_set.insert('%');
operator_set.insert('^');
return operator_set.find(ch) != operator_set.end();
}
/**
* 比较两个操作符的优先级
*/
int compare_priority(char a, char b) {
map<char, int> operator_priority;
operator_priority.insert(make_pair('+', 1));
operator_priority.insert(make_pair('-', 1));
operator_priority.insert(make_pair('*', 2));
operator_priority.insert(make_pair('/', 2));
operator_priority.insert(make_pair('%', 2));
operator_priority.insert(make_pair('^', 3));
return operator_priority[a]-operator_priority[b];
}
/**
* 判断是否有不匹配的括号
*/
bool is_bracket_valid(string infix) {
stack<char> bracket;
for(int i=0; i<infix.size(); i++) {
if(infix[i]=='(') {
bracket.push(infix[i]);
} else if(infix[i]==')') {
if(bracket.empty()) {
cout<<"有右括号没有匹配"<<endl;
return false;
}
bracket.pop();
}
}
if(!bracket.empty()) {
cout<<"有左括号没有匹配"<<endl;
return false;
}
return true;
}
/**
* 预处理
* 参数:
* infix:预处理前的中序表达式
* 返回值:
* 处理后的中序表达式
* 步骤:
* 1. 去除空格
* 2. 判断是否含有不能处理的字符
* 3. 处理一元操作符 '+'和'-'
*/
string preprocess(const string infix) {
string result = infix;
//去除空格
size_t index;
while((index = result.find(string(" "))) != string::npos) {
result.erase(index, 1);
}
cout<<"去除空格后 "<<result<<""<<endl;
//初始化能够处理的字符集合
set<char> valid_char_set;//能够处理的字符
for(int i=0; i<=9; i++) {
valid_char_set.insert(i+'0');
}
valid_char_set.insert('+');
valid_char_set.insert('-');
valid_char_set.insert('*');
valid_char_set.insert('/');
valid_char_set.insert('%');
valid_char_set.insert('^');
valid_char_set.insert('(');
valid_char_set.insert(')');
valid_char_set.insert('e');//'e'和'E'为科学计数法
valid_char_set.insert('E');
valid_char_set.insert('.');//小数点
for(int i=0; i<result.size(); i++) {
//如果字符不在valid_char_set,说明有不能处理的字符,结束程序
if(valid_char_set.find(result[i]) == valid_char_set.end()) {
cout<<"中缀表达式中有非法字符: "<<result[i]<<" "<<i<<endl;
exit(-1);
}
}
//处理一元操作符
for(int i=0; i<result.size(); i++) {
char temp = result[i];
if(temp != '+' && temp != '-') {
continue;
}
// cout<<i<<" "<<result<<" "<<result[i]<<endl;
if(i==0) { //表达式开始位置的 '-'
result.insert(i, 1, 0+'0');
} else if(i-1>=0 && !isdigit(result[i-1]) && result[i-1]!=')') { //一元+-,紧跟着其他符号后面
result.insert(i, "(0");
int j = i+3;
int bracket_count=0;//如果有括号,应包含括号
for(; j<result.size(); j++) {
if(isdigit(result[j]) || result[j]=='.') {
continue;
} else if(result[j]=='(') {
++bracket_count;
} else if(result[j]==')') {
// --left_bracket_count;
if((--bracket_count) == 0) {
break;
}
} else if(bracket_count==0) {
break;
}
}
i = j;
result.insert(j, ")");
}
}
return result;
}
/**
* 中缀表达式-->后缀表达式
*/
queue<string> infix_to_post(string infix) {
queue<string> postfix;//后缀表达式队列
stack<char> operator_stack;//转换过程中,用来存储操作符的栈
set<char> valid_operand_set;//操作数 中的字符
for(int i=0; i<=9; i++) {
valid_operand_set.insert(i+'0');
}
valid_operand_set.insert('.');
valid_operand_set.insert('e');
valid_operand_set.insert('E');
for(int i=0; i<infix.size(); i++) {
cout<<endl;
char ch = infix[i];
cout<<"序号:"<<" "<<i<<" 字符: "<<ch<<endl;
if(infix[i]=='(') {//左括号
operator_stack.push(infix[i]);
} else if(infix[i]==')') {//右括号
while(!operator_stack.empty() && operator_stack.top()!='(') {
postfix.push(string(1, operator_stack.top()));
operator_stack.pop();
}
operator_stack.pop();//将"("出栈
} else if(is_operator(infix[i]) == true) { //是操作符(不包含 左、右括号)
if(operator_stack.empty()) { //操作符栈为空
operator_stack.push(infix[i]);
continue;
}
//操作符栈非空
char top_stack = operator_stack.top();
//将栈中“较高和相等”优先级的操作符放到 后缀表达式中。
//终止条件为:当前操作符>栈顶操作符优先级,或优先级相等、但栈顶操作符的结合性是“从右向左”。
while(compare_priority(infix[i], top_stack)<=0) {
//优先级相等,但栈顶操作符结合性为从右向左,这里特指'^'
if(compare_priority(infix[i], top_stack)==0 && infix[i]=='^') { //因为'^'的结合性从右向左,所以单独处理
break;
}
//当前操作符<=栈顶操作符优先级,当前操作符结合性为从左到右
postfix.push(string(1, top_stack));
operator_stack.pop();
if(!operator_stack.empty()) {
top_stack = operator_stack.top();
} else {
break;
}
}
//将当前操作符入栈
operator_stack.push(infix[i]);
} else {//操作数
string current_operator;
int j=i;
while(valid_operand_set.find(infix[j]) != valid_operand_set.end()) {
current_operator += infix[j];
++j;
}
postfix.push(current_operator);
i=j-1;//因为for循环,每次都会执行i++
}
//打印处理过程
cout<<"当前栈顶: "<<(operator_stack.empty() ?' ': operator_stack.top())<<endl;
queue<string> temp_queue = postfix;
cout<<"当前后缀表达式: ";
while(temp_queue.size()>0) {
cout<<temp_queue.front()<<" ";
temp_queue.front();
temp_queue.pop();
}
cout<<endl;
}
//最后将栈中内容全部取出来
while(!operator_stack.empty()) {
postfix.push(string(1, operator_stack.top()));
operator_stack.pop();
}
return postfix;
}
/**
* 计算两个操作数
*/
double calculate_two(double a, double b, string operand) {
double result;
if(operand == "+") {
result = a+b;
} else if(operand == "-") {
result = a-b;
} else if(operand == "*") {
result = a*b;
} else if(operand == "/") {
if(b==0) {
cout<<"除数不能为0"<<endl;
exit(-1);
}
result = a/b;
} else if(operand == "%") {
result = (static_cast<int>(a)) % (static_cast<int>(b));
} else if(operand == "^") {
result = pow(a, b);
}
return result;
}
//对后缀表达式,进行计算
double calculate_post(queue<string>& post) {
stack<double> result_stack;
while(!post.empty()) {
string temp = post.front();
post.pop();
if(is_operator(temp[0])) { //是操作符
if(result_stack.size()<2) {
cout<<"表达式错误"<<endl;
exit(-1);
}
//从栈中取出两个元素,计算并将结果压入栈中
double operand2 = result_stack.top();
result_stack.pop();
double operand1 = result_stack.top();
result_stack.pop();
double m = calculate_two(operand1, operand2, temp);
result_stack.push(m);
} else { //操作数
double temp_operand = atof(temp.c_str());
result_stack.push(temp_operand);
}
}
return result_stack.top();
}
int main(int argc, char **argv) {
string infix;
// infix = "10e2+3*4-5%2-2^-(4/2)+.2 + 2^1^3";//结果为:1012.95
cout<<"请输入中缀表达式: "<<endl;
getline(cin, infix);
cout<<endl<<"原始前缀表达式: "<<infix<<endl;
//1. 检测括号是否匹配
bool valid = is_bracket_valid(infix);
if(valid){
cout<<endl<<"括号匹配"<<endl<<endl;
}else{
return 0;
}
//2.预处理中缀表达式
string result_infix = preprocess(infix);
cout<<"预处理以后:"<<result_infix<<endl;
//3. 中缀 转 后缀
queue<string> result_post = infix_to_post(result_infix);
//打印后缀表达式
queue<string> temp = result_post;
cout<<"后缀表达式: ";
while(!temp.empty()) {
cout<<temp.front()<<" ";
temp.pop();
}
cout<<endl;
//4. 计算结果
double result = calculate_post(result_post);
cout<<endl<<"计算结果: "<<result<<endl;
return 0;
}
运行结果:
请输入中缀表达式:
10e2+3*4-5%2-2^-(4/2)+.2 + 2^1^3
原始前缀表达式: 10e2+3*4-5%2-2^-(4/2)+.2 + 2^1^3
括号匹配
去除空格后 10e2+3*4-5%2-2^-(4/2)+.2+2^1^3
预处理以后:10e2+3*4-5%2-2^(0-(4/2))+.2+2^1^3
序号: 0 字符: 1
当前栈顶:
当前后缀表达式: 10e2
序号: 4 字符: +
序号: 5 字符: 3
当前栈顶: +
当前后缀表达式: 10e2 3
序号: 6 字符: *
当前栈顶: *
当前后缀表达式: 10e2 3
序号: 7 字符: 4
当前栈顶: *
当前后缀表达式: 10e2 3 4
序号: 8 字符: -
当前栈顶: -
当前后缀表达式: 10e2 3 4 * +
序号: 9 字符: 5
当前栈顶: -
当前后缀表达式: 10e2 3 4 * + 5
序号: 10 字符: %
当前栈顶: %
当前后缀表达式: 10e2 3 4 * + 5
序号: 11 字符: 2
当前栈顶: %
当前后缀表达式: 10e2 3 4 * + 5 2
序号: 12 字符: -
当前栈顶: -
当前后缀表达式: 10e2 3 4 * + 5 2 % -
序号: 13 字符: 2
当前栈顶: -
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2
序号: 14 字符: ^
当前栈顶: ^
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2
序号: 15 字符: (
当前栈顶: (
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2
序号: 16 字符: 0
当前栈顶: (
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2 0
序号: 17 字符: -
当前栈顶: -
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2 0
序号: 18 字符: (
当前栈顶: (
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2 0
序号: 19 字符: 4
当前栈顶: (
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2 0 4
序号: 20 字符: /
当前栈顶: /
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2 0 4
序号: 21 字符: 2
当前栈顶: /
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2 0 4 2
序号: 22 字符: )
当前栈顶: -
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2 0 4 2 /
序号: 23 字符: )
当前栈顶: ^
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2 0 4 2 / -
序号: 24 字符: +
当前栈顶: +
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2 0 4 2 / - ^ -
序号: 25 字符: .
当前栈顶: +
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2 0 4 2 / - ^ - .2
序号: 27 字符: +
当前栈顶: +
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2 0 4 2 / - ^ - .2 +
序号: 28 字符: 2
当前栈顶: +
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2 0 4 2 / - ^ - .2 + 2
序号: 29 字符: ^
当前栈顶: ^
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2 0 4 2 / - ^ - .2 + 2
序号: 30 字符: 1
当前栈顶: ^
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2 0 4 2 / - ^ - .2 + 2 1
序号: 31 字符: ^
当前栈顶: ^
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2 0 4 2 / - ^ - .2 + 2 1
序号: 32 字符: 3
当前栈顶: ^
当前后缀表达式: 10e2 3 4 * + 5 2 % - 2 0 4 2 / - ^ - .2 + 2 1 3
后缀表达式: 10e2 3 4 * + 5 2 % - 2 0 4 2 / - ^ - .2 + 2 1 3 ^ ^ +
计算结果: 1012.95
Press any key to continue.
本文链接:http://blog.csdn.net/daheiantian/archive/2011/03/19/6553713.aspx