一、效果展示
如图1所示,是简单的四则运算测试效果,第一列为原始表达式,第二列为转换后的后缀表达式,冒号后为结果。表达式支持负数和空格,图中是使用了5组测试数据,测试结果可能不全,如大家发现算法有问题,可留言,谢谢。
图1 四则运算展示
测试代码如下
1 void lineedit::CalculateExpression() 2 { 3 QString reExp("1 + 2.3 * (23 + 3)"); 4 QString res = change(reExp);//0 1 - 2.3 23 3 + * + 5 6 QString reExp2("1*(-3)+2*(3+3)"); 7 QString res2 = change(reExp2); 8 9 QString reExp3("2*-3+-2.1*(3+3)"); 10 repairExpress(reExp3); 11 QString res3 = change(reExp3); 12 13 QString reExp4("2*(-3)+-2.1*(3+3)"); 14 repairExpress(reExp4); 15 QString res4 = change(reExp4); 16 17 QString reExp5("2*(0-(1.1-3)*3)+-2.1*(3+3)"); 18 repairExpress(reExp5); 19 QString res5 = change(reExp5); 20 21 qDebug() << reExp << '\t'<< res << ":" << CalExp(res.split(' ', QString::SkipEmptyParts)); 22 qDebug() << reExp2 << '\t'<< res2 << ":" << CalExp(res2.split(' ', QString::SkipEmptyParts)); 23 qDebug() << reExp3 << '\t'<< res3 << ":" << CalExp(res3.split(' ', QString::SkipEmptyParts)); 24 qDebug() << reExp4 << '\t'<< res4 << ":" << CalExp(res4.split(' ', QString::SkipEmptyParts)); 25 qDebug() << reExp5 << '\t'<< res5 << ":" << CalExp(res5.split(' ', QString::SkipEmptyParts)); 26 }
二、一些小技巧
在网上找了很多四则运算帖子,讲的都挺不错,思路很清晰,可是很少有拿来直接能用的,并且大多数的都不支持负数运算,既然是四则运算当然需要支持负数运算了,在这里我们只需要使用一点儿小技巧即可。
1、针对负号进行字符串修复 例如:-1*-3+2*(3+3) -> (0-1)*(0-3)+2*(3+3)。
1 //针对负号进行字符串修复 例如:-1*-3+2*(3+3) -> (0-1)*(0-3)+2*(3+3) 2 void repairExpress(QString & express) 3 { 4 bool repair = false; 5 int lpos = -1, rpos = -1; 6 QString result; 7 for(int i = 0; i < express.size(); ++i) 8 { 9 QChar c = express[i]; 10 if (c == '+' || c == '-' || c == '*' || c == '/')//出现符号时记录 11 { 12 if (repair) 13 { 14 result.append(')'); 15 lpos = -1; 16 repair = false; 17 } 18 19 if (c == '-'&& 20 (i == 0 || lpos != -1 && lpos == i - 1)) 21 { 22 result.append('('); 23 repair = true; 24 } 25 26 lpos = i; 27 } 28 29 result.append(c); 30 } 31 32 express = result; 33 }
2、为了方便后续我们计算表达式,在中缀表达式转后缀表达式时,我们在数字和负号之间加了一个空格。
1 //数字和负号之间插入空格, 方便后续计算时分割 2 void rettifyExpress(QString & express) 3 { 4 if (express.endsWith(' ') == false) 5 { 6 express.append(' '); 7 } 8 }
三、后缀表达式
中缀表达式:是一个通用的算术或逻辑公式表示方法, 操作符是以中缀形式处于操作数的中间(例:3 + 4),中缀表达式是人们常用的算术表示方法。
后缀表达式:后缀表达式,指的是不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则)。
中缀表达式转后缀表达式的方法:
1.遇到操作数:直接输出(添加到后缀表达式中)
2.栈为空时,遇到运算符,直接入栈
3.遇到左括号:将其入栈
4.遇到右括号:执行出栈操作,并将出栈的元素输出,直到弹出栈的是左括号,左括号不输出。
5.遇到其他运算符:加减乘除:弹出所有优先级大于或者等于该运算符的栈顶元素,然后将该运算符入栈
6.最终将栈中的元素依次出栈,输出。
下边我直接给出实现代码
1 //中缀表达式转后缀表达式 2 QString change(const QString & s_mid) 3 { 4 QString result; 5 QStack<QChar> stk; 6 7 QMap<QChar, int> op;//利用map来实现运算符对应其优先级 8 op['(']=0; 9 op[')']=0; 10 op['+']=1; 11 op['-']=1; 12 op['*']=2; 13 op['/']=2; 14 auto iter = s_mid.begin(); 15 for(int i = 0; i < s_mid.size(); ++i) 16 { 17 QChar c = s_mid[i]; 18 if (c == ' ') 19 { 20 continue; 21 } 22 if (c == '-' && 23 (i == 0 || op.contains(s_mid[i-1])))//可能为负号 24 { 25 result.append('0'); 26 } 27 if(op.contains(c))//判断该元素是否为运算符 28 { 29 if(c == ')')//情况2 30 { 31 while(stk.top() != '(') 32 { 33 rettifyExpress(result); 34 result.append(stk.top()); 35 stk.pop(); 36 } 37 stk.pop(); 38 } 39 else if(stk.empty() || c == '(' || op[c] > op[stk.top()])//情况1、情况3 40 { 41 stk.push(c); 42 } 43 else if(op[c] <= op[stk.top()])//情况3 44 { 45 while(op[c] <= op[stk.top()] && (!stk.empty())) 46 { 47 rettifyExpress(result); 48 result.append(stk.top()); 49 stk.pop(); 50 if(stk.empty()) break; 51 } 52 stk.push(c); 53 } 54 55 rettifyExpress(result); 56 } 57 else 58 { 59 result.append(c); 60 } 61 } 62 63 while(stk.empty() == false)//当中缀表达式输出完成,所有元素出栈 64 { 65 rettifyExpress(result); 66 result.append(stk.top()); 67 stk.pop(); 68 } 69 70 return result; 71 }
四、表达式计算
通过后缀表达式计算时,我们就不需要考虑优先级了,只需要严格按照从左向右,遇到负号取之前的两个数值进行计算即可。
1 //计算表达式值 2 double CalExp(const QStringList & express) 3 { 4 double result; 5 QStack<QString> stk; 6 for (int i = 0; i < express.size(); ++i) 7 { 8 QString item = express[i]; 9 if (item.size() == 1 && 10 (item.at(0) == "+" || item.at(0) == "-" || item.at(0) == "*" || item.at(0) == "/")) 11 { 12 double r = stk.pop().todouble(); 13 double l = stk.pop().todouble(); 14 switch(item.at(0).tolatin1()) 15 { 16 case '+': 17 result = l + r;break; 18 case '-': 19 result = l - r;break; 20 case '*': 21 result = l * r;break; 22 case '/': 23 result = l / r;break; 24 } 25 26 stk.push_back(QString::number(result)); 27 } 28 else 29 { 30 stk.push_back(item); 31 } 32 } 33 34 return result; 35 }
五、下载链接
参考文章: