概述
表达式一般由操作数(Operand)、运算符(Operator)组成,例如算术表达式中,通常把运算符放在两个操作数的中间,
这称为中缀表达式(Infix Expression),如A+B。
波兰数学家Jan Lukasiewicz提出了另一种数学表示法,它有两种表示形式:
把运算符写在操作数之前,称为波兰表达式(Polish Expression)或前缀表达式(Prefix Expression),如+AB;
把运算符写在操作数之后,称为逆波兰表达式(Reverse Polish Expression)或后缀表达式(Suffix Expression),如AB+;
其中,逆波兰表达式在编译技术中有着普遍的应用。
算法
一、 将中缀表达式转换成后缀表达式算法:
1、从左至右扫描一中缀表达式。
2、若读取的是操作数,则判断该操作数的类型,并将该操作数存入操作数堆栈
3、若读取的是运算符
(1) 该运算符为左括号”(“,则直接存入运算符堆栈。
(2) 该运算符为右括号”)”,则输出运算符堆栈中的运算符到操作数堆栈,直到遇到左括号为止。
(3) 该运算符为非括号运算符:
(a) 若运算符堆栈栈顶的运算符为括号,则直接存入运算符堆栈。
(b) 若比运算符堆栈栈顶的运算符优先级高或相等,则直接存入运算符堆栈。
(c) 若比运算符堆栈栈顶的运算符优先级低,则输出栈顶运算符到操作数堆栈,并将当前运算符压入运算符堆栈。
4、当表达式读取完成后运算符堆栈中尚有运算符时,则依序取出运算符到操作数堆栈,直到运算符堆栈为空。
二、逆波兰表达式求值算法:
1、循环扫描语法单元的项目。
2、如果扫描的项目是操作数,则将其压入操作数堆栈,并扫描下一个项目。
3、如果扫描的项目是一个二元运算符,则对栈的顶上两个操作数执行该运算。
4、如果扫描的项目是一个一元运算符,则对栈的最顶上操作数执行该运算。
5、将运算结果重新压入堆栈。
6、重复步骤2-5,堆栈中即为结果值。
实现
将中缀表达式转后缀表达式
将人们熟悉的中缀表达式转换为后缀表达式,便于计算机的计算,这里需要用到栈数据结构。
// 定义符号优先级var symbolMap = map[string]int{"+": 1,"-": 1,"*": 2,"/": 2,"(": 3,")": 3,}func main() {// 待解析的表达式var str string = "1+1+(2+3)+3-3+4"// 检查括号是否对齐bracketCheck(str)suffixExp := trans2SuffixExp(str)fmt.Println("后缀表达式:", suffixExp)// 计算ret := parseExpression(str)fmt.Printf("表达式[%s]=%d", str, ret)}func trans2SuffixExp(exp string) string {var resultStr = ""var stack = InitStack(20)for _, e := range exp {ele := string(e)if unicode.IsNumber(e) {resultStr += elecontinue}// 操作符if stack.isEmpty() {stack.Push(ele)continue}// 将栈顶元素比当前元素优先级高的出栈,追加到表达式后面topEleStr := fmt.Sprintf("%v", stack.TopEle())if ele != ")" && symbolMap[topEleStr] < symbolMap[ele] {stack.Push(ele)continue}// 栈顶是左括号直接入栈if "(" == topEleStr {stack.Push(ele)continue}if ")" == ele {// 弹出栈顶元素,直到遇到(for {topEleStr := fmt.Sprintf("%v", stack.Pop())if topEleStr == "(" {break}resultStr += topEleStr}} else {// 弹出栈顶优先级大于等于当前元素的符号for {topEleStr = fmt.Sprintf("%v", stack.TopEle())if symbolMap[topEleStr] < symbolMap[ele] {break}// 追加resultStr += fmt.Sprintf("%v", stack.Pop())}// 当前符号入栈stack.Push(ele)}}// 弹出栈内所有元素for !stack.isEmpty() {resultStr += fmt.Sprintf("%v", stack.Pop())}return resultStr}
计算后缀表达式的结果
上小结已经得到了表达式的后缀表达式,那么我们可以直接利用后缀表达式计算结果了,这里也需要用到栈数据结构
func calSuffixExp(exp string) interface{} {// 根据后缀表达式计算numStack := InitStack(20)for _, e := range exp {if unicode.IsNumber(e) {num, _ := strconv.ParseInt(string(e), 0, 64)numStack.Push(num)continue}// 符号cal(string(e), numStack)}result := numStack.Pop()return result}func cal(symbol string, stack *Stack) {sec, _ := stack.Pop().(int64)first, _ := stack.Pop().(int64)if "+" == symbol {stack.Push(first + sec)} else if "-" == symbol {stack.Push(first - sec)} else if "*" == symbol {stack.Push(first * sec)} else if "/" == symbol {stack.Push(first / sec)}}
测试
func main() {// 待解析的表达式var str string = "1+1+(2+3)+3-3+4"fmt.Println("中缀表达式:", str)// 检查括号是否对齐bracketCheck(str)suffixExp := trans2SuffixExp(str)fmt.Println("后缀表达式:", suffixExp)result := calSuffixExp(suffixExp)fmt.Println("后缀表达式计算结果:", result)}
输出
中缀表达式: 1+1+(2+3)+3-3+4 后缀表达式: 11+23++3+3-4+ 后缀表达式计算结果: 11
通过中缀表达式一步计算
上文我们通过先将中缀表达式转后缀表达式完成后再计算,这里用到了两个栈帧,一个符号栈,一个数字栈,我们如果结合两个栈空间同样可以一步计算到位(过程中有转后缀及计算的含义)
func parseExpression(str string) Element {symbolStack := InitStack(20)numStack := InitStack(20)var currNum = ""for _, ch := range str {ele := string(ch)if unicode.IsNumber(ch) {currNum += elecontinue}// 操作数入栈if currNum != "" {num, _ := strconv.ParseInt(currNum, 0, 32)numStack.Push(num)currNum = ""}if symbolStack.isEmpty() || "(" == ele {// 第一个操作符直接入栈symbolStack.Push(ele)continue}topSymbol := symbolStack.TopEle()var symbolStr = fmt.Sprintf("%v", topSymbol)// 栈顶操作符的优先级更低,当前操作符直接入栈if symbolMap[symbolStr] < symbolMap[ele] && ")" != ele {symbolStack.Push(ele)continue}if "(" == symbolStr {symbolStack.Push(ele)continue}// 当前操作符优先级较高if ")" == ele {for {// 弹出栈顶操作符直到遇到“(”symbolStr = fmt.Sprintf("%v", symbolStack.Pop())if symbolStr == "(" {break}cal(symbolStr, numStack)}} else {for {symbolStr = fmt.Sprintf("%v", symbolStack.Pop())if symbolStr == "<nil>" {break} else if symbolMap[symbolStr] < symbolMap[ele] {symbolStack.Push(symbolStr)break}cal(symbolStr, numStack)}}// 当前操作符入栈if ")" != ele {symbolStack.Push(ele)}//}if currNum != "" {num, _ := strconv.ParseInt(currNum, 0, 32)numStack.Push(num)currNum = ""}// 计算剩余表达式for !symbolStack.isEmpty() {symbolStr := fmt.Sprintf("%v", symbolStack.Pop())cal(symbolStr, numStack)}// 操作数栈中栈顶元素就为结果:ret := numStack.Pop()return ret}func cal(symbol string, stack *Stack) {sec, _ := stack.Pop().(int64)first, _ := stack.Pop().(int64)if "+" == symbol {stack.Push(first + sec)} else if "-" == symbol {stack.Push(first - sec)} else if "*" == symbol {stack.Push(first * sec)} else if "/" == symbol {stack.Push(first / sec)}}
