数据结构--栈

192 阅读4分钟

栈的一种Go语言实现

package main

import (
	"errors"
	"fmt"
)

type Stack struct {
	MaxTop int //栈的最大容量
	Top int //栈顶
	arr [5]int
}

func (s *Stack) Push(val int) (err error) {
	//判断栈是否已满
	if s.Top == s.MaxTop - 1 {
		fmt.Println("stack full")
		return errors.New("stack full")
	}

	s.Top++
	s.arr[s.Top] = val
	return
}

func (s *Stack) Pop() (val int, err error) {
	//判断栈是否为空
	if s.Top == -1 {
		fmt.Println("stack empty")
		return -1, errors.New("stack empty")
	}

	val = s.arr[s.Top]
	s.Top--
	return val, nil
}

func (s *Stack) List() {
	//判断栈是否为空
	if s.Top == -1 {
		fmt.Println("stack empty")
		return
	}

	//从栈顶开始遍历
	for i := s.Top; i >= 0; i-- {
		fmt.Printf("arr[%d]=%d\n", i, s.arr[i])
	}
}

func main(){
	stack := &Stack{
		MaxTop: 5,
		Top:    -1,
	}

	//入栈
	stack.Push(1)
	stack.Push(2)
	stack.Push(3)
	stack.Push(4)
	stack.Push(5)
	stack.Push(6)

	//显示
	stack.List()

	//弹出
	val, _ := stack.Pop()
	val, _ = stack.Pop()
	val, _ = stack.Pop()
	val, _ = stack.Pop()
	val, _ = stack.Pop()
	//val, _ = stack.Pop()
	fmt.Println("出栈val=", val)
	stack.List()
}

应用案例:

问题:计算一串字符串表达式

思路:

1.创建两个栈,一个是存放数字的栈,一个是存放操作符的栈,记为numStack, operStack;

2.如果扫描字符串时发现是数字,则直接入数栈;

3.如果发现是一个运算符则分为两种情况:

3.1如果操作符栈operStack是一个空栈,则直接入栈;

3.2如果操作符栈operStack不是一个空栈则按照操作符的优先级分为两种情况计算:

​ 3.2.1如果发现operStack栈顶的运算符的优先级大于等于当前准备入栈的运算符的优先级,就从符号栈pop出操作符,同时从数栈pop出两个数进行运算,将运算后的结果再重新入栈到数栈,再将当前准备入栈的符号入栈到操作符栈;

​ 3.2.2 否则,运算符直接入栈;

4.如果扫描表达式完毕,依次从符号栈取出符号,然后从数栈取出两个数,将运算后的结果再入栈,直到符号栈为空,此时数栈中的值为表达式的计算结果。

package main

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

type expStack struct {
	MaxTop int //栈的最大容量
	Top int //栈顶
	arr [20]int
}

func (s *expStack) Push(val int) (err error) {
	//判断栈是否已满
	if s.Top == s.MaxTop - 1 {
		fmt.Println("stack full")
		return errors.New("stack full")
	}

	s.Top++
	s.arr[s.Top] = val
	return
}

func (s *expStack) Pop() (val int, err error) {
	//判断栈是否为空
	if s.Top == -1 {
		fmt.Println("stack empty")
		return -1, errors.New("stack empty")
	}

	val = s.arr[s.Top]
	s.Top--
	return val, nil
}

func (s *expStack) List() {
	//判断栈是否为空
	if s.Top == -1 {
		fmt.Println("stack empty")
		return
	}

	//从栈顶开始遍历
	for i := s.Top; i >= 0; i-- {
		fmt.Printf("arr[%d]=%d\n", i, s.arr[i])
	}
}

//判断一个字符是否是运算符
func (s *expStack) IsOper(val int) bool {
	if val == 42 || val == 43 || val == 45 || val == 47 {
		return true
	}else {
		return false
	}
}

//运算方法
func (s *expStack) 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
}

//返回运算符的优先级,自定义
func (s *expStack) Priority(oper int) int {
	res := 0
	if oper == 42 || oper == 47 {
		res = 1 //自定义
	}else if oper == 43 || oper == 45 {
		res = 0
	}else {
		fmt.Println("其他情况")
	}
	return res
}

func main(){
	//数栈
	numStack := &expStack{
		MaxTop: 20,
		Top:    -1,
	}
	//符号栈
	operStack := expStack{
		MaxTop: 20,
		Top:    -1,
	}

	exp := "30+20*6-2"
	index := 0
	num1, num2, oper := 0, 0, 0
	keepNum := ""

	for {
		//处理多位数问题

		ch := exp[index:index+1] //"3"
		temp := int([]byte(ch)[0]) //字符对应的ASCII码值
		if operStack.IsOper(temp) { //是符号
			//如果是空栈,直接入栈
			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 { //说明是数
			//定义一个变量keepNum 做字符串拼接处理多位数
			//每次要向index的后面字符测试一下,看看是不是运算法
			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)
}