表达式求值(中缀表达式——后缀表达式——求值)
来源:互联网 发布:linux 扫描磁盘的命令 编辑:程序博客网 时间:2024/06/16 00:16
表达式求值,属于数据结构——栈的典型应用。使用后缀表达式的与原因,是因为在求值的过程中,不需要考虑操作符的优先级。(结合性仍需要考虑)
但是一般的书上只讲到如何处理二元操作符,并且结合性都是从左到右结合的。这里的实现能够处理一元操作符,并且能够处理从右向左结合的幂乘'^'操作符。
功能需求
给定一个中缀表达式,求出该表达式的值。
要求支持“加减乘除 + - * /、取余 %、幂运算 ^、一元正负号 + -、括号 ()、数的科学表示法符号 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.95Press any key to continue.
本文链接:http://blog.csdn.net/daheiantian/archive/2011/03/19/6553713.aspx
- 表达式求值(中缀表达式——后缀表达式——求值)
- 栈的应用——中缀表达式转后缀表达式,后缀表达式的求值,中缀表达式求值
- 中缀 后缀表达式求值
- C++栈的应用——后缀表达式求值、中缀表达式到后缀表达式的转换
- 表达式求值(中缀转后缀及后缀表达式求值)
- 【表达式求值】中缀表达式转变为后缀表达式
- 中缀表达式求值、后缀表达式求值、中缀转后缀、前缀
- 表达式求值(中缀变后缀再求值)
- 表达式求值(前缀、中缀、后缀)
- 表达式求值(中缀表达式转换为后缀表达式)
- 中缀表达式转后缀表达式并求值
- 中缀表达式转后缀表达式求值
- 中缀表达式转后缀表达式并求值
- 中缀表达式转后缀表达式求值
- 中缀表达式转后缀表达式并求值
- 中缀表达式变后缀表达式并求值
- 中缀表达式转后缀表达式并求值
- 中缀表达式转后缀表达式 求值
- pl/sql之分页
- linux epoll模型
- Memcached(二):Memcached的结构、存储规则探索
- Oracle 10gR2 RAC & Hacmp 要求
- AIX hacmp 配置文档
- 表达式求值(中缀表达式——后缀表达式——求值)
- 1.腾讯微博Android客户端开发——OAuth认证介绍
- Log4j在项目中的应用
- 2.腾讯微博Android客户端开发——Parameter类和SyncHttp类编写
- C#获取指定数据库服务器下某用户可访问的数据库列表
- 3.腾讯微博Android客户端开发——算法、编码、辅助方法编写
- HDU 1171 Big Event in HDU
- aix errpt DUPLICATE IP ADDRESS DETECTED IN THE NET
- 4.腾讯微博Android客户端开发——获取未授权的Request Token