使用程序实现四则混合运算(含括号)
最近在开发一款数字运算类游戏,需要对玩家输入的算式计算出结果。乍一听,感觉简直不能太简单了,必定我们都用了那么多年的计算器,也在草纸上演算过那么多年了……。可是用编程的方式来实现真的会那么简单吗?
面临的问题
- 乘、除运算的优先级要高于加减运算
- 带有小括号的运算高于乘除
计算机发展到今天,看似这么原始的运算,我们聪明的古人肯定早就实现了。我怀着无比崇拜的心情阅读了逆波兰表达式
波兰数学家扬·武卡谢维奇1920年引入的数学表达式方式
操作符(+-×÷)置于操作数的后面,因此也被称为后缀表示法。逆波兰记法不需要括号来标识操作符的优先级。
PS 我们传统的数学表达式,运算符号在2个数字中间,所以也叫中缀表达式
逆波兰记法中(后缀标识法),操作符置于操作数的后面,例如 3 + 4 会表示为:3 4 + ,3+3÷1 会表示为3 3 1 ÷ +
1. 后缀表达式的计算方式
规则:
对后缀表达式的每个数字或符号,从左至右的顺序,
- step1. 遇到数字就进栈
- step2. 遇到符号就将栈顶的2个元素,出栈-->运算-->再将结果压栈
- 一直重复上述2步,直到后缀表达式都遍历一遍
此时栈会只有一个元素,这便是运算的结果。
下面我们用一个实际的例子来演示下后缀表达式和后缀表达式的计算方式
我们求:3+3÷1
对应的后缀表达式:3,3,1,÷,+ (至于怎么将中缀表达式转成后置表达式,稍后有讲解)
2. 中缀表达式转后缀表达式
规则:
1.当当前字符为数字时,直接输出
2.当当前字符为(时,将(压栈
3.当当前字符为)时,则弹出堆栈中最上的(之前的所有运算符,并输出,然后删除堆栈中的(
4.当当前字符为运算符时,则依次弹出堆栈中优先级大于等于当前运算符的[到(之前为止],输出,再将当前运算符压栈
5.最后弹出所有栈中的内容输出
示例:将5+(3x2)-1 转为后缀表达式
中缀表达式转后缀表达式的算法
/**
* @param {*} ary 数学表达式Array ,如["5","+","(","3","×","2",")","-","1"]
* @returns 得到后缀表达式,如["5","3","2","×","+","1","-"]
*/
getEndSign(ary){
let outAry=[];
let operateAry=[];
let itemV;
let loopValue=false;
let poped;
for(let i=0;i<ary.length;i++)
{
itemV=Number(ary[i]);
if(MathUtil.NkIsNumber(itemV)){
//如果是数字,直接输出
outAry.push(itemV);
}else if(ary[i]=="("){
operateAry.push("(");
}else if(ary[i]==")"){
loopValue=true;
while (loopValue) {
poped=operateAry.pop();
if(poped=="("){
loopValue=false;
}else{
outAry.push(poped);
}
}
}else{
//操作符,依次把所有优先级>=这个运算符的都弹出,并输出
//再将当前的操作符,压栈
if(ary[i]=="+"||ary[i]=="-"){
loopValue=true;
while (loopValue) {
if(operateAry.length>0){
if (operateAry[operateAry.length - 1] == "(") {
loopValue = false;
// operateAry.pop();
} else {
outAry.push(operateAry.pop());
}
}else{
loopValue=false;
}
}
operateAry.push(ary[i]);
}else{
loopValue=true;
while (loopValue) {
if(operateAry.length>0){
if (operateAry[operateAry.length - 1] == "(") {
loopValue = false;
// operateAry.pop()
} else if(operateAry[operateAry.length - 1]=="*"||operateAry[operateAry.length - 1]=="×"||operateAry[operateAry.length - 1]=="/"||operateAry[operateAry.length - 1]=="÷") {
outAry.push(operateAry.pop());
}else{
loopValue=false;
}
}else{
loopValue=false;
}
}
operateAry.push(ary[i]);
}
}
}
while (operateAry.length>0) {
outAry.push(operateAry.pop());
}
return outAry;
}