1. 中缀、前缀、后缀表达式
对于一个人可识别的表达式:1+(2+3)*4-5
根据操作符的位置不同分为:
①中缀表达式:1+(2+3)*4-5
②前缀表达式:- + 1 * + 2 3 4 5
③后缀表达式:1 2 3 + 4 * + 5 -
前缀表达式和后缀表达式里面已经包含了计算顺序,因此不需要括号来确定优先级
2. 中缀转前缀
2.1 中缀转前缀
①按运算符优先级对所有的运算单位加括号
((1+((2+3)*4))-5)
②将运算符移动到对应括号的前面
- ( + ( 1 * ( + 2 3 ) 4 )) 5 )
③去掉括号,得到前缀表达式
- + 1 * + 2 3 4 5
转换的计算机实现:
(1)表达式树
(2)栈
①两个栈,运算符栈S1、存储中间结果栈S2
②从右到左扫描表达式
③遇到操作数,压栈S2
④遇到运算符,比较其与S1栈顶运算符优先级
若S1为空,或栈顶为右括号 ')' ,则此运算符入栈S1
若优先级比栈顶运算符高或相等,则此运算符入栈S1
若比栈顶优先级低,将S1栈顶运算符出栈压到S2里面,然后继续与S1栈顶运算符比较。重复④
⑤遇到括号
右括号直接压入S1
左括号,则依次弹出S1栈顶运算符到S2,直到遇到右括号,此时将这一对括号丢弃
⑥重复直到表达式最左边
⑦将S1剩余运算符依次弹出压到S2
⑧依次弹出S2中元素,得到前缀表达式
1 string midexToPreex(string &preex) 2 { 3 set<char> ops = {'+','-','*','/'}; 4 map<char,int> priority = {{'+',1},{'-',1},{'*',2},{'/',2}}; 5 ostringstream strstream; 6 // string *res = new string(); 7 stack<node> s1,s2; 8 int preex_len = preex.size(); 9 struct node temp; 10 for(int i=preex_len-1;i>=0;--i) { 11 if(preex[i] == ' ' || preex[i] == '\t') { 12 continue; 13 } 14 else if((preex[i]>='0' && preex[i]<='9') || preex[i] =='.') { //遇到操作数 15 ostringstream strtmp; 16 stack<char> num_tmp; 17 if(preex[i] != '.') { //对于15. 这种数,去掉后面的点,看作int类型 18 num_tmp.push(preex[i]); 19 } 20 21 // strtmp<<preex[i]; 22 for(int j=i-1;j>=0;j--) { //这里还需要修改 23 if(preex[j]>='0'&& preex[j]<='9' || preex[j] =='.') { 24 num_tmp.push(preex[j]); 25 // strtmp<<preex[j]; 26 i--; 27 } 28 else if(preex[j] == ' ' || preex[j] == '\t') { 29 i--; 30 } 31 else { 32 break; 33 } 34 } 35 while(!num_tmp.empty()) { 36 strtmp<<num_tmp.top(); 37 num_tmp.pop(); 38 } 39 temp.num = strtmp.str(); 40 temp.flag = true; 41 s2.push(temp); 42 } 43 else if(ops.find(preex[i]) != ops.end()) { //如果是运算符 44 if(s1.empty() || (!s1.top().flag && s1.top().op==')')) { 45 temp.flag = false; 46 temp.op = preex[i]; 47 s1.push(temp); 48 } 49 else if(priority[preex[i]] >= priority[s1.top().op]) { 50 temp.flag = false; 51 temp.op = preex[i]; 52 s1.push(temp); 53 } 54 else if(priority[preex[i]] < priority[s1.top().op]) { 55 while(!(s1.empty() || s1.top().op==')' || priority[preex[i]] >= priority[s1.top().op])) { 56 s2.push(s1.top()); 57 s1.pop(); 58 } 59 temp.flag = false; 60 temp.op = preex[i]; 61 s1.push(temp); 62 } 63 } 64 else if(preex[i] == ')') { 65 temp.flag = false; 66 temp.op = preex[i]; 67 s1.push(temp); 68 } 69 else if(preex[i] == '(') { 70 while(s1.top().op != ')') { 71 s2.push(s1.top()); 72 s1.pop(); 73 } 74 s1.pop();//丢弃右括号 75 //此左括号不作处理,相当于丢弃了 76 } 77 else { 78 break; 79 } 80 } 81 while(!s1.empty()) { 82 s2.push(s1.top()); 83 s1.pop(); 84 } 85 while(!s2.empty()) { 86 if(s2.top().flag) { 87 strstream<<s2.top().num<<" "; 88 } 89 else { 90 strstream<<s2.top().op<<" "; 91 } 92 s2.pop(); 93 } 94 return strstream.str(); 95 }
2.2 前缀表达式解析计算
①从右到左扫描表达式
②遇到数字,则数字压栈,遇到运算符,取出栈顶的两个数做运算:栈顶 op 次顶,结果入栈
③重复直到表达式最左侧
1 template<typename T> 2 T cal(T n1,T n2,char op) 3 { 4 T res; 5 switch(op) { 6 case '+':res=n1+n2;break; 7 case '-':res=n1-n2;break; 8 case '*':res=n1*n2;break; 9 case '/':res=n1/n2;break; 10 default:break; 11 } 12 return res; 13 } 14 15 int cal_prerc(string &preex) 16 { 17 int ex_size = preex.size(); 18 set<char> ops = {'+','-','*','/'}; 19 bool int_or_double = true; //true代表表达式中只有int 20 if(preex.find('.') != string::npos) { 21 int_or_double = false; 22 } 23 stack<int> int_stack; 24 stack<double> double_stack; 25 for(int i = ex_size-1;i>=0;i--) { 26 if(preex[i] == ' ') { 27 continue; 28 } 29 if(preex[i]>='0'&&preex[i]<='9') { 30 stringstream tmp; 31 stack<char> tmp_stack; 32 tmp_stack.push(preex[i]); 33 for(int j=i-1;j>=0;--j) { 34 if((preex[j]>='0'&&preex[j]<='9') || preex[j]=='.') { 35 tmp_stack.push(preex[j]); 36 i--; 37 } 38 else if(preex[j] == ' ') { 39 break; 40 } 41 } 42 while(!tmp_stack.empty()) { 43 tmp<<tmp_stack.top(); 44 tmp_stack.pop(); 45 } 46 string str_tmp = tmp.str(); 47 if(int_or_double) { 48 int num; 49 tmp>>num; 50 int_stack.push(num); 51 } 52 else{ 53 double num; 54 tmp>>num; 55 double_stack.push(num); 56 } 57 } 58 else{ //如果是运算符 59 if(int_or_double) { 60 int a = int_stack.top(); 61 int_stack.pop(); 62 int b = int_stack.top(); 63 int_stack.pop(); 64 int res = cal(a,b,preex[i]); 65 int_stack.push(res); 66 } 67 else { 68 double a = double_stack.top(); 69 double_stack.pop(); 70 double b = double_stack.top(); 71 double_stack.pop(); 72 double res = cal(a,b,preex[i]); 73 double_stack.push(res); 74 } 75 } 76 } 77 if(int_or_double) { 78 int result = int_stack.top(); 79 cout<<"result:"<<result<<endl; 80 } 81 else { 82 double result = double_stack.top(); 83 cout<<"result:"<<result<<endl; 84 } 85 return 1; 86 }
3. 中缀转后缀
3.1 中缀转后缀
①按运算符优先级对所有的运算单位加括号
②将运算符移动到对应括号的后面
③去掉括号,得到前缀表达式
(1)表达式树
(2)栈
3.2 后缀表达式解析结算
①从左到右扫描表达式
②遇到数字,数字压栈,遇到运算符,取出栈顶两个数做运算:次顶 op 栈顶,结果入栈
③重复,直到表达式最右侧
4. 表达式合法性判断
(1)括号的合法性
这里的括号表达式只有 { } [ ] ( ) ,暂不算数字和运算符,且输入的表达式字符串里面没有其他无效字符
bool expreIsOK()
{
map<char,char> exmap = {{'}','{'},{']','['},{')','('}}; stack<char> char_stack; string str; getline(cin,str); cout<<"Your expression:"<<str<<endl; string::iterator iter = str.begin(); while(iter != str.end()) { if(exmap.find(*iter) != exmap.end()) { //如果是右括号 if(char_stack.empty() || char_stack.top() != exmap[*iter]) { return false; } else { char_stack.pop(); } } else { //如果是左括号 char_stack.push(*iter); } ++iter; } if(char_stack.empty()) { return true; } else { return false; } }
(2)运算符合法性
(3)运算数合法性
5. 完整的解析计算中缀表达式
①检验合法性:包括括号的合法性和运算符的合法性、运算数合法性
括号合法性:括号成对出现且遵循数学规范
运算符合法性:比如不能有两个连续的运算符
运算数合法性:这里暂忽略大数。如果遇到小数,则运算数 3.14 5. 都是合法的,但 3.14. 不合法
②中缀转成前缀
③前缀解析计算