需求:31+2*(4+15-45+4/2)-1 ,处理此字符串,完成使用栈实现计算功能
思路:
* 计算式:31+2*(4+1*5-4*5+4/2)-1
*
* 处理难点: 1.数字多位
* 2.操作符优先级设置
* 3.括号操作符处理
*
* 解决:使用两个栈来存放计算式的操作符与数字
*
* 思路:1.因为数字可能是多位字符串,则需要一个变量记录每次数字截取开始坐标
* 2.处理如上计算式为一个字符串,从前到后遍历计算式每个字符,针对每个字符进行判断处理,取出操作符与数字
* 3.对每次遍历的字符进行判断处理,如果当前字符是数字则遍历下个字符,如果是操作符则有两种情况
* 2.1 操作符前面是数字,则从数字开始坐标截取到当前坐标,如第一次0-2,将截取数字如数值栈,当前字符如操作符号栈,并将数字截取开始坐标重置为当前坐标 s=2
* 2.2 操作符前面是符号,如 *( 则直接入操作符栈,并将下次截取的数字的开始坐标设置为此坐标
* 4.操作符优先级处理,包含括号共有 5 种可能,具体如下 代码中 4. 操作符入栈
* cur :当前取出的操作符; top:符号栈栈顶操作符
*
* 注意:这里需要循环处理不然会导致计算先后顺序问题导出结果不正确
* 如下:4-5*3+4
* 如果不循环处理则变成了:4-15+4-->4-19
* 5.如果计算式最后一位不是操作符,则需要在最后进行判断最后部分是否为数字,并入数字栈
* 6.按照优先级处理完计算式后,则需要最后进栈里数据进行计算
代码:
object Calculator {
def main(args: Array[String]): Unit = {
// val valStack3 = new LinkStack02
// valStack3.push('+')
// valStack3.push('-')
// valStack3.push('*')
// println(valStack3.pop().toChar)
// println(calculator(4, 3, '*'))
println("-------------")
val i = compute("3-2*4+1+1")
println(i)
println("-------------")
val i2 = compute("31+2*(4+1*5-4*5+4/2)-1")
println(i2)
}
private val valStack = new LinkStack02 //数值栈
private val operStack = new LinkStack02 //操作符栈
// 3+2*2-4+4*(5-2)/3
/**
* 字符串计算函数
*
* @param str
* @return
*/
def compute(str: String): Int = {
//1. 遍历处理每个字符
var s = 0
for (i <- 0 until str.length) {
val str1 = str.substring(i, i + 1)
//2.判断字符是否为数字
if (isVal(str1)) {
//println("字符不是操作符:"+str1)
} else {
//3. 数字入栈
if (isVal(str.substring(s, i))) {
val value = str.substring(s, i)
valStack.push(value.toInt)
}
//4. 操作符入栈
val oper = str.substring(i, i + 1).charAt(0)
//4.1 判断操作符栈是否为空
if (operStack.isEmpty()) {
operStack.push(oper)
} else {
//4.2 符号栈不为空:比较操作符大小,cur <= top,则取出栈顶操作符计算
var flag = true;
//4.3 循环处理
while (flag) {
//4.4 判断符号栈不为空,并且 cur <= top
if (operStack.size > 0 && priority(oper) <= priority(operStack.getTop.toChar)) {
//4.4.1 栈顶是( 当前不是 ) ,则可以判断这是括号内的第一个操作符,则直接入栈,并终止循环处理下一个字符
if (operStack.getTop == '(' && oper != ')') {
operStack.push(oper)
flag = false
//4.4.2 可能一:不是括号内的计算
//4.4.2 可能二:是括号内的计算,但是可以判断这是括号内的第一个操作符 及 ) 以前的操作符
} else if (operStack.getTop != '(' && oper != ')') {
val top = operStack.pop.toChar //操作符
val num1 = valStack.pop //后
val num2 = valStack.pop //前
val res = calculator(num2, num1, top)
valStack.push(res) //计算结果入栈
//4.4.3 可判断出到达括号边界,需要将括号里面的算式先计算完成
} else if (operStack.getTop != '(' && oper == ')') {
val top = operStack.pop.toChar //操作符
val num1 = valStack.pop //后
val num2 = valStack.pop //前
val res = calculator(num2, num1, top)
valStack.push(res) //计算结果入栈
} else {
//4.4.4 那就只剩下最后一种情况括号内的算式已经计算完成,则需要将 ( 出栈并终止循环,开始处理下一个字符
// if(operStack.getTop =='(' && oper == ')')
val top = operStack.pop.toChar //操作符
flag = false
}
} else {
//4.4.5. cur > top 操作符入栈,终止循环处理下一个字符
operStack.push(oper)
flag = false
}
}
}
s = i + 1 //下一次截取位置
}
}
//最后一部分是数字入栈
if (isVal(str.substring(s, str.length))) {
valStack.push(str.substring(s, str.length).toInt)
}
while (operStack.size > 0) {
val p1 = operStack.pop.toChar //操作符
val num1 = valStack.pop //后
val num2 = valStack.pop //前
val res = calculator(num2, num1, p1)
valStack.push(res) //计算结果入栈
}
valStack.pop()
}
/**
* 计算
*
* @param n1 数1
* @param n2 数2
* @param oper 操作符
* @return
*/
def calculator(n1: Int, n2: Int, oper: Char): Int = {
var res = 0
println(n1 + "" + oper + "" + n2)
oper match {
case '+' => res = n1 + n2
case '-' => res = n1 - n2
case '*' => res = n1 * n2
case '/' => res = n1 / n2
}
res
}
/**
* 判断是否是数值
*
* @param s
* @return
*/
def isVal(s: String): Boolean = {
var res = true
try {
val int = s.toInt
} catch {
case ex: NumberFormatException => {
// println(ex.getMessage)
res = false
}
} finally {
}
res
}
/**
* 自定义操作符优先级
*
* @param c
* @return
*/
def priority(c: Char): Int = {
if ('+'.equals(c) || '-'.equals(c)) {
1
} else if ('*'.equals(c) || '/'.equals(c)) {
2
} else if ('('.equals(c)) {
3
} else if (')'.equals(c)) {
0
} else {
-1
}
}
}