目录
介绍
本文通过栈来进行实现综合的计算器,在控制台输入要进行运算的表达式,通过计算将结果输出。
算法思路
- 创建两个栈,numStack栈用于存放表达式中的数字,简称数栈。operStack栈用于存放表达式中的运算符,简称符号栈。
- exp:存储控制台输入的表达式;index:帮助扫描exp表达式。
- 通过扫描exp发现是数字,则直接入数栈numStack
- 如果发现是一个运算符。(1)如果operStack是一个空栈,直接入栈。(2)如果operStack不是一个空栈:1)如果发现operStack栈顶运算符的优先级大于等于当前准备入栈的运算符的优先级,就从符号栈Pop出,并从数栈也Pop两个数进行运算,运算后的结果重新入栈到数栈,符号再入符号栈。2)否则,运算符直接入栈
- 如果扫描表达式完毕,依次从符号栈取出符号,然后从数栈取出两个数,运算后的结果,入数栈,直到符号栈为空。
算法实现
本文运用的栈为:Golang实现数组模拟栈和队列-CSDN博客,并在此基础栈中添加一些计算器相关的方法。
判断一个字符是不是运算符
" * "的ASCII码为42;" + "的ASCII码为43; " - "的ASCII码为45; " / "的ASCII码为47;
func (s *Stack) IsOper(val int) bool {
if val == 42 || val == 43 || val == 45 || val == 47 {
return true
} else {
return false
}
}
运算的方法
参数为数栈的两个元素,符号栈对应的ASCII码的符号
func (s *Stack) Cal(num1, num2, oper int) int {
res := 0
switch oper {
case 42:
res = num2 * num1
case 43:
res = num2 + num1
case 45:
res = num2 - num1
case 47:
res = num2 / num1
default:
fmt.Println("运算符错误!")
}
return res
}
运算符的优先级
返回运算符的优先级: * / 优先级为 1; + - 优先级为 0
func (s *Stack) Priority(oper int) int {
if oper == 42 || oper == 47 {
return 1
} else if oper == 43 || oper == 45 {
return 0
}
return 0
}
核心算法
创建数栈和符号栈,定义exp存储表达式。
numStack := &Stack{
MaxTop: 20,
Top: -1,
}
operStack := &Stack{
MaxTop: 20,
Top: -1,
}
var exp string
fmt.Println("请输入一个只包含(+,-,*,/)的表达式")
fmt.Scanln(&exp)
定义num1,num2为数栈要计算的两个数,oper为符号栈的运算符的ASCII码,result为运算结果;keepNum为处理多位数。
// 定义一个index,帮助扫描exp
index := 0
//为了配合运算,我们定义需要的变量
num1 := 0
num2 := 0
oper := 0
result := 0
keepNum := ""
定义ch用于接收exp的字符,temp将字符装换为ASCII码
ch := exp[index : index+1]
temp := int([]byte(ch)[0]) //就是字符对应的ASCII码
判断temp是运算符还是数字
这里判断是运算符的情况:首先判断符号栈是不是空栈如果是直接入栈,如果不是,就将要入栈的运算符与符号栈的栈顶运算符进行比较,如果要入栈的运算符的优先级小就直接入栈,否则从数栈中弹出两个数,从符号栈弹出运算符进行运算。
if operStack.IsOper(temp) {
// 说明是符号
// 如果operStack 是一个空栈,直接入栈
if operStack.Top == -1 {
operStack.Push(temp)
} else {
// 如果发现operStack栈顶的运算符的优先级大于等于当前准备入栈的运算符的优先级,就从符号栈Pop出,并从数栈中也Pop两个数,进行运算
// 运算后的结果再重新入栈到数栈,当前符号再入符号栈
if operStack.Priority(operStack.arr[operStack.Top]) >= operStack.Priority(temp) {
num1 = numStack.Pop()
num2 = numStack.Pop()
oper = operStack.Pop()
result = operStack.Cal(num1, num2, oper)
// 将计算结果重新入栈
numStack.Push(result)
// 当前的符号压入栈
operStack.Push(temp)
} else {
//否则直接将符号入栈
operStack.Push(temp)
}
}
}
这里判断是数字的情况,并且包含多位数的情况。定义变量keepNum进行拼接多位数,然后每次要向index的前面字符测试一下,是不是运算符,然后处理。如果已经到表达式最后,直接keepNum
else {
keepNum += ch
if index == len(exp)-1 {
val, _ := strconv.ParseInt(keepNum, 10, 64)
numStack.Push(int(val))
} else {
if operStack.IsOper(int([]byte(exp[index+1 : index+2])[0])) {
val, _ := strconv.ParseInt(keepNum, 10, 64)
numStack.Push(int(val))
keepNum = ""
}
}
}
判断index 是不是扫描到表达式的最后
if index+1 == len(exp) {
break
}
index++
如果扫描表达式 完毕 ,依次从符号栈取出符号,然后从数栈取出两个数,运算后的结果,入数栈,直到符号栈为空
for {
if operStack.Top == -1 {
break
}
num1 = numStack.Pop()
num2 = numStack.Pop()
oper = operStack.Pop()
result = operStack.Cal(num1, num2, oper)
// 将计算结果重新入栈
numStack.Push(result)
}
// 如果算法没有问题,表达式也正确,则结果就是numStack最后数
res := numStack.Pop()
fmt.Printf("表达式%s=%v", exp, res)
核心算法的整体代码
// 数栈
numStack := &Stack{
MaxTop: 20,
Top: -1,
}
operStack := &Stack{
MaxTop: 20,
Top: -1,
}
var exp string
fmt.Println("请输入一个只包含(+,-,*,/)的表达式")
fmt.Scanln(&exp)
// 定义一个index,帮助扫描exp
index := 0
//为了配合运算,我们定义需要的变量
num1 := 0
num2 := 0
oper := 0
result := 0
keepNum := ""
for {
//增加一个逻辑,处理多位数的问题
ch := exp[index : index+1]
temp := int([]byte(ch)[0]) //就是字符对应的ASCII码
if operStack.IsOper(temp) {
// 说明是符号
// 如果operStack 是一个空栈,直接入栈
if operStack.Top == -1 {
operStack.Push(temp)
} else {
// 如果发现operStack栈顶的运算符的优先级大于等于当前准备入栈的运算符的优先级,就从符号栈Pop出,并从数栈中也Pop两个数,进行运算
// 运算后的结果再重新入栈到数栈,当前符号再入符号栈
if operStack.Priority(operStack.arr[operStack.Top]) >= operStack.Priority(temp) {
num1 = numStack.Pop()
num2 = numStack.Pop()
oper = operStack.Pop()
result = operStack.Cal(num1, num2, oper)
// 将计算结果重新入栈
numStack.Push(result)
// 当前的符号压入栈
operStack.Push(temp)
} else {
//否则直接将符号入栈
operStack.Push(temp)
}
}
} else {
// 说明是数字
//处理多位数的思路
//1.定义一个变量 keepNum string ,做拼接
keepNum += ch
//2.每次要向index的前面字符测试一下,是不是运算符,然后处理
//如果已经到表达式最后,直接keepNum
if index == len(exp)-1 {
val, _ := strconv.ParseInt(keepNum, 10, 64)
numStack.Push(int(val))
} else {
// 向index后面测试一下看看是不是运算符
if operStack.IsOper(int([]byte(exp[index+1 : index+2])[0])) {
val, _ := strconv.ParseInt(keepNum, 10, 64)
numStack.Push(int(val))
keepNum = ""
}
}
}
//继续扫描
//先判断index 是否扫描到计算表达式的最后
if index+1 == len(exp) {
break
}
index++
}
// 如果扫描表达式 完毕 ,依次从符号栈取出符号,然后从数栈取出两个数
//运算后的结果,入数栈,直到符号栈为空
for {
if operStack.Top == -1 {
break
}
num1 = numStack.Pop()
num2 = numStack.Pop()
oper = operStack.Pop()
result = operStack.Cal(num1, num2, oper)
// 将计算结果重新入栈
numStack.Push(result)
}
// 如果算法没有问题,表达式也正确,则结果就是numStack最后数
res := numStack.Pop()
fmt.Printf("表达式%s=%v", exp, res)
整体代码
package main
import (
"fmt"
"strconv"
)
type Stack struct {
MaxTop int //表示栈中能存放的最多的元素
Top int // 表示栈顶元素
arr [20]int
}
// Push 入栈
func (s *Stack) Push(val int) {
if s.Top+1 == s.MaxTop {
fmt.Println("栈满了")
return
}
s.Top++
s.arr[s.Top] = val
}
// Push2 一次性添加多个元素
func (s *Stack) Push2(v ...int) {
if s.Top+1 == s.MaxTop {
fmt.Println("栈满了")
return
}
for _, val := range v {
s.Push(val)
}
}
// Pop 出栈
func (s *Stack) Pop() int {
if s.Top == -1 {
fmt.Println("空栈")
return 0
}
v := s.arr[s.Top]
s.Top--
return v
}
// Peek 显示栈中元素
func (s *Stack) Peek() {
if s.Top == -1 {
fmt.Println("空栈")
return
}
fmt.Println("栈中元素为:")
for i := s.Top; i >= 0; i-- {
fmt.Printf("arr[%d]=%d\n", i, s.arr[i])
}
}
// IsOper 判断一个字符是不是一个运算符[+,-,*,/]
func (s *Stack) IsOper(val int) bool {
if val == 42 || val == 43 || val == 45 || val == 47 {
return true
} else {
return false
}
}
// Cal 运算的方法
func (s *Stack) Cal(num1, num2, oper int) int {
res := 0
switch oper {
case 42:
res = num2 * num1
case 43:
res = num2 + num1
case 45:
res = num2 - num1
case 47:
res = num2 / num1
default:
fmt.Println("运算符错误!")
}
return res
}
// Priority 编写一个方法,返回某个运算符的优先级
// * / 优先级 1
// + - 优先级 0
func (s *Stack) Priority(oper int) int {
if oper == 42 || oper == 47 {
return 1
} else if oper == 43 || oper == 45 {
return 0
}
return 0
}
func main() {
// 数栈
numStack := &Stack{
MaxTop: 20,
Top: -1,
}
operStack := &Stack{
MaxTop: 20,
Top: -1,
}
var exp string
fmt.Println("请输入一个只包含(+,-,*,/)的表达式")
fmt.Scanln(&exp)"
// 定义一个index,帮助扫描exp
index := 0
//为了配合运算,我们定义需要的变量
num1 := 0
num2 := 0
oper := 0
result := 0
keepNum := ""
for {
//增加一个逻辑,处理多位数的问题
ch := exp[index : index+1]
temp := int([]byte(ch)[0]) //就是字符对应的ASCII码
if operStack.IsOper(temp) {
// 说明是符号
// 如果operStack 是一个空栈,直接入栈
if operStack.Top == -1 {
operStack.Push(temp)
} else {
// 如果发现operStack栈顶的运算符的优先级大于等于当前准备入栈的运算符的优先级,就从符号栈Pop出,并从数栈中也Pop两个数,进行运算
// 运算后的结果再重新入栈到数栈,当前符号再入符号栈
if operStack.Priority(operStack.arr[operStack.Top]) >= operStack.Priority(temp) {
num1 = numStack.Pop()
num2 = numStack.Pop()
oper = operStack.Pop()
result = operStack.Cal(num1, num2, oper)
// 将计算结果重新入栈
numStack.Push(result)
// 当前的符号压入栈
operStack.Push(temp)
} else {
//否则直接将符号入栈
operStack.Push(temp)
}
}
} else {
// 说明是数字
//处理多位数的思路
//1.定义一个变量 keepNum string ,做拼接
keepNum += ch
//2.每次要向index的前面字符测试一下,是不是运算符,然后处理
//如果已经到表达式最后,直接keepNum
if index == len(exp)-1 {
val, _ := strconv.ParseInt(keepNum, 10, 64)
numStack.Push(int(val))
} else {
// 向index后面测试一下看看是不是运算符
if operStack.IsOper(int([]byte(exp[index+1 : index+2])[0])) {
val, _ := strconv.ParseInt(keepNum, 10, 64)
numStack.Push(int(val))
keepNum = ""
}
}
}
//继续扫描
//先判断index 是否扫描到计算表达式的最后
if index+1 == len(exp) {
break
}
index++
}
// 如果扫描表达式 完毕 ,依次从符号栈取出符号,然后从数栈取出两个数
//运算后的结果,入数栈,直到符号栈为空
for {
if operStack.Top == -1 {
break
}
num1 = numStack.Pop()
num2 = numStack.Pop()
oper = operStack.Pop()
result = operStack.Cal(num1, num2, oper)
// 将计算结果重新入栈
numStack.Push(result)
}
// 如果算法没有问题,表达式也正确,则结果就是numStack最后数
res := numStack.Pop()
fmt.Printf("表达式%s=%v", exp, res)
}