QT简易计算器--表达式计算核心算法(二)
来源:互联网 发布:java 查看线程状态 编辑:程序博客网 时间:2024/05/16 15:08
概述:上篇我主要介绍了用QT做计算器的整个流程,这次主要就是分析一下,计算器表达式计算的算法部分。因为也找了很多别人写的代码,但大多都是只支持个位数的加减乘除,小数也不支持,所以就在原有框架上,修改,优化,让其满足我想要的功能。
1,表达式计算思路。
表达式数据操作符分割–>转为逆波兰表达式–>计算逆波兰表达式值–>输出结果。
2,表达式计算详解。
(1)表达式数据操作符分割
首先我们得到了一个字符串表达式,我们将数据和操作符分割。
如:表达式—————> “23+1.23-(2-5)*3”
分割后为“23” “+” “1.23” “- ”“( ”“2”“ -” “5” “)”“*” “3”,然后将其存入一个QString数组,这样数据,操作符分割就便于后面计算,辨别是数据还是操作符了。遍历数组,只要第一个字符不是0-9就是操作符了。
下面是这部分代码详解:
/*将表达式的数据,操作符分割,依次存入mask_buffer数组中*/int Calculator::mask_data(QString expression, QString *mask_buffer){ int i,k = 0,cnt = 0; QString::iterator p = expression.begin();//获取表达式头指针 int length = expression.length();//获取表达式数据长度 /*for 循环遍历整个表达式,进行数据,操作符分割*/ for(i = 0 ; i < length ; i += cnt,k++) { cnt = 0; if(*p >= '0' && *p <= '9') { /*当第一个字符为0-9时,肯定是数字*/ QString temp = *p; p ++; cnt ++; /*看数字后面是否还是数字或小数点,直到遇到下个操作符,这个数据才结束*/ while((*p >= '0' && *p <= '9') || *p == '.') { temp += *p; p++; cnt ++; } mask_buffer[k] = temp; }else{ /*不是数字,就是操作符,直接写入数组即可*/ QString temp = *p; p++; cnt ++; mask_buffer[k] = temp; } } return k;}
(2)转为逆波兰表达式。
既然用到逆波兰表达式,首先了解一下什么是逆波兰表达式。我们正常的表达式是中缀表达式,逆波兰就是将中缀表达式转化为后缀表达式。我们就简单粗暴一些,例子。
1+2*3-(2+5)———>中缀表达式。
1,2,3,*,+,2,5,+,- ———>后缀表达式。
中缀到后缀如何转化呢,下面就是详细的规则了:
首先我们定义一个数组A,堆栈B。数组A用于存储最后的后缀表达式,堆栈B就是一个过渡器,帮助我们转化,再定义操作符的优先级,如+ - * / ( )。
从左到右遍历整个表达式。
1,遇到数字加入数组A。
2,遇到左括号直接入栈到B。
3,遇到右括号,堆栈B出栈,加入到数组A中,直到遇到左括号,将左括号出栈但不加入数组A中。
4,如果遇到的是运算符:
(1)当堆栈B为空,直接入栈到B。
(2)当堆栈不为空,当前运算符优先级大于堆顶运算符优先级,入栈到B。
(3)当堆栈不为空,当前运算符优先级小于等于堆顶运算符优先级,B出栈到A数组中,直到堆顶运算符优先级小于当前运算符优先级,再将当前运算符入栈B。
5,当遍历完整个表达式,堆栈B不为空,依次出栈加入到A数组中。
下面详细步骤转化过程:
数组A[100],堆栈B.表达式:1+2*3-(2+5)遍历:"1" ------> A{"1"} B{空}"+" ------> A{"1"} B{"+"}"2" ------> A{"1","2"} B{"+"}"*" ------> A{"1","2"} B{"+","*"} //"*"优先级高于"+""3" ------> A{"1","2","3"} B{"+","*"}"-" ------> A{"1","2","3","*","+"} B{"-"} //"-"优先级不大于"*",不大于"+",*,+出栈到A"(" ------> A{"1","2","3","*","+"} B{"-","("} "2" ------> A{"1","2","3","*","+","2"} B{"-","("}"+" ------> A{"1","2","3","*","+","2"} B{"-","(","+"}"5" ------> A{"1","2","3","*","+","2","5"} B{"-","(","+"}")" ------> A{"1","2","3","*","+","2","5","+"} B{"-"} //遇到右括号,+出栈到A中 A{"1","2","3","*","+","2","5","+","-"} B{空} //遍历结束,B中不为空,"-"出栈到A中
下面是这部分源码:
/*获取操作符优先级*/int Calculator::Priority(QString data){ int priority; if(data == "(") priority = 1; else if(data == "+" || data == "-") priority = 2; else if(data == "*" || data == "/") priority = 3; else if (data == ")") priority = 4; else priority = -1; return priority;}/*将获取到的分割好的表达式数组,转化为逆波兰表达式,存入数组repolish中*/int Calculator::re_polish(QString *mask_buffer,QString *repolish,int length){ QStack<QString> st2; int i = 0; for(int j = 0 ; j < length ; j++) { if(mask_buffer[j] != "(" && mask_buffer[j] != ")" && mask_buffer[j] != "+" && mask_buffer[j] != "-" && mask_buffer[j] != "*" && mask_buffer[j] != "/" ) repolish[i++] = mask_buffer[j]; else if(mask_buffer[j] == "("){ st2.push(mask_buffer[j]); } else if(mask_buffer[j] == ")"){ while(st2.top() != "(") { repolish[i++] = st2.top(); st2.pop(); } if(st2.top() == "(") st2.pop(); } else if(st2.empty() || Priority(mask_buffer[j]) > Priority(st2.top())) st2.push(mask_buffer[j]); else{ while(Priority(mask_buffer[j]) <= Priority(st2.top())) { repolish[i++] = st2.top(); st2.pop(); if(st2.empty()) break; } st2.push(mask_buffer[j]); } } while(!st2.empty()) { repolish[i++] = st2.top(); st2.pop(); } return i;}
(3)计算逆波兰表达式值。
计算就比较简单了,我们只需要维护一个保存数据的堆栈就行了,我们有一个逆波兰表达式数组,上面得到的,再新建一个堆栈st。遍历整个逆波兰表达式数组,遇到数字直接压栈到st中,遇到“+”,“-”,“*”,“/”,就取出st栈顶元素a,st.pop(),取下一个栈顶元素b,st.pop()。计算数值对应操作符值(如操作符为+,b+a),将计算的值入栈到st中。直到表达式遍历完,堆栈里只剩一个数据,就是最后的结果值,取出。
我们还是以 1,2,3,*,+,2,5,+,- 为例。
遍历数组A[] = {"1","2","3","*","+","2","5","+","-"} "1" ------> st{1} "2" ------> st{1,2} "3" ------> st{1,2,3} "*" ------> st{1,6} //2*3 = 6 "+" ------> st{7} //1+6 = 7 "2" ------> st{7,2} "5" ------> st{7,2,5} "+" ------> st{7,7} //2+5 = 7 "-" ------> st{0} //7-7 = 0 即最后结果为0
下面是这部分源码:
/*计算逆波兰表达式值并显示*/double Calculator::repolish_calculat(QString *repolish,int length){ QStack <double> st; for(int m = 0 ; m < length ; m ++) { if(repolish[m] != "+" && repolish[m] != "-" && repolish[m] != "*" && repolish[m] != "/" ) { /*Qstring转化为double数据存入堆栈*/ st.push(repolish[m].toDouble()); } else { if(repolish[m] == "+") { double a = st.top(); st.pop(); double b = st.top(); st.pop(); st.push(b + a); } else if(repolish[m] == "-") { double a = st.top(); st.pop(); double b = st.top(); st.pop(); st.push(b - a); } else if(repolish[m] == "*") { double a = st.top(); st.pop(); double b = st.top(); st.pop(); st.push(b * a); } else if(repolish[m] == "/") { double a = st.top(); st.pop(); double b = st.top(); st.pop(); if(a != 0) st.push(b/a); else { ui->display->clear(); ui->put_data->setText("0 不能做除数"); return -1; } } } } QString res = QString::number(st.top(),'g',10); ui->display->clear(); ui->put_data->setText(res); return st.top();}
3,项目完整源码在上篇中有链接,需要的话可直接下载。
- QT简易计算器--表达式计算核心算法(二)
- 表达式求值(简易计算器的核心)
- 计算器核心算法(二)
- C# 简易计算器(二)
- 算法学习之递归--表达式计算(简单计算器)
- 简易计算器(核心算法就是Shutting-Yard, 逆波兰式)
- Qt实现简易计算器
- Qt之简易计算器
- <Qt>实例:简易计算器
- Qt简易计算器
- 简易计算器(逆波兰算法)
- Qt实现的简单计算器-计算简单表达式
- 计算器核心算法(一)
- linux 下Qt初级教程(一)实现简易的计算器
- Qt学习之路十一(一个简易计算器)
- 表达式计算(二)
- 表达式计算器的核心代码
- Qt计算器开发(一):后缀表达式实现完整数学表达式的计算
- TreeSet的排序原理
- 一对一查询(9)
- 51NOD 1091 线段重叠 51 NOD 1133不重叠线段 贪心
- luogu1890 gcd区间(线段树/预处理)
- 【BOOST】boost : : noncopyable 分析,拷贝构造、赋值函数
- QT简易计算器--表达式计算核心算法(二)
- Java与Python对比学习1
- eclipse设置编码格式
- POJ1308 Is It A Tree?
- 【二分匹配】【匈牙利算法即由增广路求最大匹配模板】
- Java发送邮件
- Java面试题大汇总
- 遗传算法的初步学习(一)
- Java中为什么匿名对象中方法,调用局部变量必须加final呢?