中缀表达式求解
问题描述:
中缀表达式计算,只包含加减乘除以及括号,例如:
2+3*4-5/2 = 11.5
((2+3*4)-5)/2 = 4.5
思路:
1. 维护两个栈空间:数字栈与符号栈
2. 注意入栈与出栈的规则,符号栈入栈时,根据符号优先级判断是否入栈或出栈。
高优先级符号后入栈时直接放入即可,低优先级符号后入栈时先弹出高优先级符号,优先级如下:
( < +- < * / < )
普通操作符弹出计算时,只进行一次pop_cal操作。
右括号出栈时,必须循环计算至左括号出栈为止。
边界case:
1. 分母为0,assert处理。
2. 数字范围超出变量上下限。
知识点:
1. 栈操作:围绕出栈入栈操作分解问题,分解成cal计算函数、push_xxx压栈操作、pop_cal出栈计算操作、push_check入栈检查优先级等几步,逻辑会更清晰。
2. 字符处理:用stringstream简化处理逻辑,string -> float, float -> string,多次使用之间需要clear() & str(""),重置标识以及清空字符串两个操作。
难度:★★★★
1. 代码量大
2. 需要考虑的细节多:字符处理、边界case、符号优先级、右括号特殊处理
3. 熟悉栈操作
伪码:
1 #include <stack> 2 #include <string> 3 #include <sstream> 4 #include <iostream> 5 #include <assert.h> 6 #include <ctype.h> 7 8 using namespace std; 9 10 stack<string> nums; //数字栈 11 stack<char> exps; //操作符栈 12 13 //压入数字栈 14 void push_num(char exp, bool is_new) 15 { 16 if( is_new ) 17 { 18 string new_num = string("") + exp; 19 nums.push( new_num ); 20 } 21 else 22 { 23 string top_num = nums.top(); 24 nums.pop(); 25 top_num += exp; 26 nums.push( top_num ); 27 } 28 } 29 30 //得到优先级 31 int get_priority(char exp) 32 { 33 switch( exp ) 34 { 35 case '*': 36 return 2; 37 case '/': 38 return 2; 39 case '+': 40 return 1; 41 case '-': 42 return 1; 43 case '(': 44 return 0; 45 case ')': 46 return -1; 47 } 48 return 0; 49 } 50 51 //检查入栈优先级 52 bool push_check(char exp) 53 { 54 if( exps.size() == 0 ) 55 { 56 return true; 57 } 58 int cur_prio = get_priority(exp); 59 int top_prio = 3; 60 char top_exp = exps.top(); 61 top_prio = get_priority(top_exp); 62 if( cur_prio < top_prio ) 63 { 64 return false; 65 } 66 return true; 67 } 68 69 //数字出栈 70 void pop_nums(float &num1, float &num2) 71 { 72 stringstream ss; 73 string num = nums.top(); 74 ss << num; 75 ss >> num1; 76 nums.pop(); 77 ss.clear(); 78 ss.str(""); 79 num = nums.top(); 80 ss << num; 81 ss >> num2; 82 nums.pop(); 83 } 84 85 //出栈一次并计算结果,返回当前出栈操作符 86 char pop_cal(float &res) 87 { 88 res = 0.0; 89 char exp = exps.top(); 90 exps.pop(); 91 if( exp != '(' ) 92 { 93 float num1, num2; 94 pop_nums(num1, num2); 95 switch( exp ) 96 { 97 case '*': 98 res = num2 * num1; 99 break; 100 case '/': 101 res = num2 / num1; 102 break; 103 case '+': 104 res = num2 + num1; 105 break; 106 case '-': 107 res = num2 - num1; 108 break; 109 } 110 } 111 return exp; 112 } 113 114 //压入操作符栈 115 void push_exp(char exp) 116 { 117 while( ! push_check(exp) ) 118 { 119 float res; 120 char pop_exp = pop_cal(res); 121 if( pop_exp != '(' ) 122 { 123 string num; 124 stringstream ss; 125 ss << res; 126 ss >> num; 127 nums.push( num ); 128 } 129 if( exp == ')' && pop_exp == '(' ) 130 break; 131 } 132 if( exp != ')' ) 133 { 134 exps.push( exp ); 135 } 136 } 137 138 //主函数:计算中缀表达式 139 float cal_res(const string &express) 140 { 141 //是否新数字 142 bool new_num = true; 143 //循环读取表达式字符 144 for(size_t i = 0; i < express.size(); i++) 145 { 146 //处理数字 147 if( isdigit( express[i] ) || express[i] == '.' ) 148 { 149 //数字压栈 150 push_num(express[i], new_num); 151 new_num = false; 152 } 153 else 154 { 155 //操作符压栈 156 push_exp( express[i] ); 157 new_num = true; 158 } 159 } 160 //循环出栈,计算结果 161 float res = 0.0; 162 while( exps.size() > 0 ) 163 { 164 pop_cal( res ); 165 } 166 return res; 167 } 168 169 int main() 170 { 171 string expression = "(3.2+4.0/2.1)*5.0"; 172 cout << cal_res( expression ) << endl; 173 return 0; 174 }
转载请注明引用自:
http://www.cnblogs.com/breakthings/p/4049575.html