一、效果展示
如图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.最终将栈中的元素依次出栈,输出。下边我直接给出实现代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 //中缀表达式转后缀表达式 2 QString change(const QString & s_mid) 3 { 4 QString result; 5 QStackstk; 6 7 QMap 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 == ')')//情况230 {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、情况340 {41 stk.push(c);42 }43 else if(op[c] <= op[stk.top()])//情况344 {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 else58 {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 QStackstk; 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 }
五、下载链接
参考文章:
1、
2、