算术表达式

好久没写博客,最近想把数据结构与算法好好学下,在这里记录下。希望能坚持下去!

前缀表达式(操作符在操作数之前)是一种没有括号的表达式。也叫波兰式

中缀表达式(操作符在操作数之间)是我们平时最常见的表达式,计算规则就是先乘除后加减,有括号,先计算括号里面的表达式,再括号外。

后缀表达式(操作符在操作数后面)是一种已经考虑了运算符优先级,没有括号的表达式。也叫逆波兰式

需求:

  • 新建数据结构栈(Stack)
  • 将中缀表达式转化为后缀表达式
  • 计算后缀表达式

此算法使用Golang实现。

package main

import (
	"errors"
	"fmt"
	"strconv"
)

// 栈
type ArrayStack struct {
	// 栈的大小
	maxStack int
	// 模拟栈
	stack []interface{}
	// 当前栈所在的位置
	top int
}

func NewStack(maxStack int) *ArrayStack {
	return &ArrayStack{
		maxStack: maxStack,
		stack:    make([]interface{}, maxStack),
		top:      -1,
	}
}

/*
1. 压栈
2. 弹栈
3. 判断是否是空栈
4. 判断是否是满栈
5. 查看栈顶数据
6. 打印栈列表
*/

// 是否为满栈
func (as *ArrayStack) IsFull() bool {
	return as.top == as.maxStack-1
}

// 是否为空栈
func (as *ArrayStack) IsEmpty() bool {
	return as.top == -1
}

// 弹栈
func (as *ArrayStack) Pop() (interface{}, error) {

	if as.IsEmpty() {
		fmt.Println("该栈为空")
		return nil, errors.New("该栈为空")
	}
	value := as.stack[as.top]
	as.top--
	return value, nil
}

// 压栈
func (as *ArrayStack) Push(value interface{}) error {

	if as.IsFull() {
		fmt.Println("该栈已满")
		return errors.New("该栈已满")
	}
	as.top++
	as.stack[as.top] = value
	return nil
}

// 查看栈顶的值
func (as *ArrayStack) Peek() interface{} {
	return as.stack[as.top]
}

// 打印栈
func (as *ArrayStack) PrintList() error {

	if as.IsEmpty() {
		fmt.Println("该栈为空,未找到任何数据")
		return errors.New("该栈为空,未找到任何数据")
	}

	for i := as.top; i >= 0; i-- {
		fmt.Printf("stack[%d]: %v\n", i, as.stack[i])
	}
	return nil
}

以上代码以实现一个栈的数据结构。接下来测试下

func main() {

	///**
	// 实例化
	stack1 := NewStack(8)

	// 压栈操作
	stack1.Push(1)
	stack1.Push(2)
	stack1.Push(3)
	stack1.Push(4)
	stack1.Push(5)
	stack1.Push("a")
	stack1.Push("b")
	stack1.Push("c")
	stack1.Push("d")
	stack1.Push("e")
	// 打印栈数据
	stack1.PrintList()
	fmt.Println("==============================")
	// 弹栈
	val, _ := stack1.Pop()
	fmt.Printf("栈弹出的值为:%v\n", val)
	fmt.Println("==============================")
	stack1.PrintList()
}

输出结果为:

此栈已满
此栈已满
stack[7]: c
stack[6]: b
stack[5]: a
stack[4]: 5
stack[3]: 4
stack[2]: 3
stack[1]: 2
stack[0]: 1
==============================
栈弹出的值为:c
==============================
stack[6]: b
stack[5]: a
stack[4]: 5
stack[3]: 4
stack[2]: 3
stack[1]: 2
stack[0]: 1

接下来继续添加函数来处理我们中缀表达式

// 是否为操作符
func IsOperator(op string) bool {
	switch op {
	case "+", "-", "*", "/", "(", ")":
		return true
	default:
		return false
	}
}

// 优先级
func Priority(op string) int {
	switch op {
	case "*", "/":
		return 2
	case "+", "-":
		return 1
	default:
		return 0
	}
}

// 计算
func Compute(op string, num1, num2 int) int {
	switch op {
	case "*":
		return num1 * num2
	case "/":
		return num1 / num2
	case "+":
		return num1 + num2
	case "-":
		return num1 - num2
	default:
		return 0
	}
}

// 字符串类型转整数类型
func string_to_int(str string) int {
	num, err := strconv.Atoi(str)

	if err != nil {
		fmt.Println("转换失败")
	}
	return num
}

/*
中缀表达式转化为后缀表达式
1. 遍历中缀表达式字符串
	1.1 如果遇到操作数就存入后缀表达式变量中
	1.2 如果遇到操作符('*','+','-','/','('),新建一个栈来存储操作符:
		1.2.1 如果是'(',压入栈中
		1.2.2 如果是')', 则将栈中的操作符存入后缀表达式变量中,直到在栈中读取到'('
		1.2.3 如果不是'(', ')', 比较该操作符与栈顶的操作符的优先级
			1.2.3.1 如果该操作符的优先级比栈顶操作符的优先级高,则入栈
			1.2.3.2 反之,将栈顶的操作符取出(pop())放入后缀表达式变量中,重复1.2.3步操作,直到将其压入栈中
2. 遍历完中缀表达式后,读取栈中数据,如栈中还有操作符,将其依次pop()取出存入后缀表达式变量中
*/

// 生成后缀表达式
func GenerateSuffixexpression(exp string) (suffixExpression string, err error) {
	// 实例化一个栈来暂存操作符
	stack := NewStack(len(exp))
	for _, e := range exp { // 1步骤
		char := string(e)

		if IsOperator(char) {
			if char == "(" { // 1.2.1
				stack.Push(char)
			} else if char == ")" { // 1.2.2
				for {
					if stack.IsEmpty() {
						err = errors.New("表达式错误,请检查!")
						return
					}
					if stack.Peek().(string) == "(" {
						stack.Pop()
						break
					}
					tmp, _ := stack.Pop()
					suffixExpression += tmp.(string)
				}
			} else { // 1.2.3
				for {
					if stack.IsEmpty() {
						break
					}
					tmp := stack.Peek().(string)
					if Priority(char) > Priority(tmp) { // 1.2.3.1
						break
					}
					suffixExpression += tmp
					stack.Pop()
				}
				stack.Push(char)
			}
		} else { // 1.1
			suffixExpression += char
		}
	}

	// 遍历完中缀表达式后,查看栈中是否还有数据,如有,则取出追加到后缀表达式变量中
	for { //2
		if stack.IsEmpty() {
			break
		}
		tmp, _ := stack.Pop()
		suffixExpression += tmp.(string)
	}
	fmt.Println("中缀表达式:", exp)
	fmt.Println("后缀表达式:", suffixExpression)
	return
}

// 计算后序表达式
func Calculate(exp string) {
	stack := NewStack(len(exp))

	for _, e := range exp {
		char := string(e)

		// 如果是运算符
		if IsOperator(char) {
			if stack.IsEmpty() {
				break
			}
			num2, _ := stack.Pop()
			num1, _ := stack.Pop()
			x1 := string_to_int(num1.(string))
			x2 := string_to_int(num2.(string))
			rs := fmt.Sprintf("%d", Compute(char, x1, x2))

			stack.Push(rs)

			fmt.Printf("Caclulate() %d %s %d = %s\n", x1, char, x2, rs)

		} else { // 如果是数字
			stack.Push(char)
			fmt.Println("Caclulate() char:", char)
		}
	}

	if !stack.IsEmpty() {
		res, err := stack.Pop()
		if err != nil {
			fmt.Println("Pop()异常")
			return
		}
		fmt.Println("表达式运算结果为:", res)
	}

}

已上就是将中缀表达式转化为后缀表达式,并计算结果有关的函数,测试如下:


func main() {
	/*使用栈实现计算字符串表达式*/
	res, err := GenerateSuffixexpression("5*(3*4+(3+2))+(5+3)/2")
	if err != nil {
		fmt.Println("异常")
	}
	fmt.Println("==============================")
	Calculate(res)

}

结果如下:

中缀表达式: 5*(3*4+(3+2))+(5+3)/2
后缀表达式: 534*32++*53+2/+
==============================
Caclulate() char: 5
Caclulate() char: 3
Caclulate() char: 4
Caclulate() 3 * 4 = 12
Caclulate() char: 3
Caclulate() char: 2
Caclulate() 3 + 2 = 5
Caclulate() 12 + 5 = 17
Caclulate() 5 * 17 = 85
Caclulate() char: 5
Caclulate() char: 3
Caclulate() 5 + 3 = 8
Caclulate() char: 2
Caclulate() 8 / 2 = 4
Caclulate() 85 + 4 = 89
表达式运算结果为: 89

Enjoy.