Go数据结构与算法之栈

概述

栈(stack)是一个先入后出(FILO-First In Last Out)的有序列表。

栈是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)。

根据堆栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除。

栈的示意图

在这里插入图片描述

应用场景

  1. 子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中。
  2. 处理递归调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数、区域变量等数据存入堆栈中。
  3. 表达式的转换与求值。
  4. 二叉树的遍历。
  5. 图形的深度优先(depth-first)搜索法。

栈的案例

基于数组实现的栈

package main

import (
	"errors"
	"fmt"
)

// Stack 基于数组实现的顺序栈
type Stack struct {
	arr   []int // 数组
	count int   // 栈中最大可以存放个数
	top   int   // 表示栈顶
}

// Push 入栈操作
func (stack *Stack) Push(val int) (err error) {
	// 判断栈是否满了
	if stack.top == stack.count-1 {
		return errors.New("stack full")
	}

	// 放入数据
	stack.top++
	stack.arr[stack.top] = val

	return
}

// Pop 出栈操作
func (stack *Stack) Pop() (val int, err error) {
	// 判断栈是否为空
	if stack.top == -1 {
		return 0, errors.New("stack empty")
	}

	// 取出数据
	val = stack.arr[stack.top]
	stack.top--

	return val, nil
}

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

	for i := stack.top; i >= 0; i-- {
		fmt.Println(i, "=>", stack.arr[i])
	}
}

func main() {
	stack := &Stack{
		arr:   make([]int, 5),
		count: 5,
		top:   -1,
	}

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

	stack.List()
	/*输出结果:
	4 => 0
	3 => 4
	2 => 3
	1 => 2
	0 => 1*/

	// 数据出栈
	val1, _ := stack.Pop()
	fmt.Println(val1) // 5
	val2, _ := stack.Pop()
	fmt.Println(val2) // 4

	stack.List()
	/*输出结果:
	2 => 3
	1 => 2
	0 => 1*/
}

栈实现综合计算器

算法思路:例如计算 exp := "3+2*6-2"

  1. 创建两个栈,numStack(数栈),operatorStack(符号栈),numStack 存放数,operatorStack 存放操作符。

  2. exp 计算表达式是一个字符串,进行依次扫描:

    (1) 如果扫描发现是一个数字,则直接入 numStack。

    (2) 如果发现是一个运算符,则进行判断:如果 operatorStack 是一个空栈,直接入栈。如果 operatorStack 不是一个空栈,如果发现 operatorStack 栈顶的运算符的优先级大于等于当前准备入栈的运算符的优先级,就从符号栈pop出,并从数栈也 pop 两个数,进行运算,运算后的结果再重新入栈到数栈,当前符号再入符号栈,否则,运算符就直接入栈。

  3. 如果扫描表达式完毕,依次从符号栈取出符号,然后从数栈取出两个数,运算后的结果,入数栈,直到符号栈为空。

在这里插入图片描述
在这里插入图片描述

package main

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

// Stack 基于数组实现的顺序栈
type Stack struct {
	arr   []int // 数组
	count int   // 栈中最大可以存放个数
	top   int   // 表示栈顶
}

// Push 入栈操作
func (stack *Stack) Push(val int) (err error) {
	// 判断栈是否满了
	if stack.top == stack.count-1 {
		return errors.New("stack full")
	}

	// 放入数据
	stack.top++
	stack.arr[stack.top] = val

	return
}

// Pop 出栈操作
func (stack *Stack) Pop() (val int, err error) {
	// 判断栈是否为空
	if stack.top == -1 {
		return 0, errors.New("stack empty")
	}

	// 取出数据
	val = stack.arr[stack.top]
	stack.top--

	return val, nil
}

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

	for i := stack.top; i >= 0; i-- {
		fmt.Println(i, "=>", stack.arr[i])
	}
}

// IsOperator 判断字符是否运算符
func (stack *Stack) IsOperator(val int) bool {
	if val == 42 || val == 43 || val == 45 || val == 47 {
		return true
	}

	return false
}

// Calculate 运算的方法
func (stack *Stack) Calculate(num1, num2, operator int) int {
	res := 0
	switch operator {
	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 返回运算符的优先级
func (stack *Stack) Priority(operator int) int {
	res := 0
	if operator == 42 || operator == 47 {
		res = 1
	} else if operator == 43 || operator == 45 {
		res = 0
	}

	return res
}

// StackCalculate 栈实现综合计算器
func StackCalculate(exp string) int {

	// 数栈
	numStack := &Stack{
		arr:   make([]int, 20),
		count: 20,
		top:   -1,
	}

	// 符号栈
	operatorStack := &Stack{
		arr:   make([]int, 20),
		count: 20,
		top:   -1,
	}

	// 定义一个index,帮助扫描exp
	index := 0

	// 配合运算定义需要的变量
	num1 := 0
	num2 := 0
	operator := 0
	result := 0
	keepNum := ""

	for {
		// 字符串
		ch := exp[index : index+1]
		// 字符对应的ASCII值
		temp := int([]byte(ch)[0])

		if operatorStack.IsOperator(temp) { // 说明是字符

			// 如果operatorStack是一个空栈,直接入栈
			if operatorStack.top == -1 {
				operatorStack.Push(temp)
			} else {

				// 如果发现operatorStack栈顶的运算符的优先级大于等于当前准备入栈的运算符的优先级
				// 就从符号栈pop出,并从数栈也pop两个数,进行运算,运算后的结果再重新入栈到数栈,当前符号再入符号栈
				if operatorStack.Priority(operatorStack.arr[operatorStack.top]) >= operatorStack.Priority(temp) {
					num1, _ = numStack.Pop()
					num2, _ = numStack.Pop()
					operator, _ = operatorStack.Pop()
					result = operatorStack.Calculate(num1, num2, operator)
					// 将计算结果重新入数栈
					numStack.Push(result)
					// 当前的符号压入符号栈
					operatorStack.Push(temp)
				} else {
					operatorStack.Push(temp)
				}
			}
		} else { // 说明是数

			// 处理多位数的问题

			// 定义一个变量keepNum string,做拼接
			keepNum += ch

			// 每次要向index的后面字符测试一下,看看是不是运算符,然后处理,如果已经到表达最后,直接将keepNum
			if index == len(exp)-1 {
				val, _ := strconv.ParseInt(keepNum, 10, 64)
				numStack.Push(int(val))
			} else {
				// 向index后面测试看看是不是运算符[index]
				if operatorStack.IsOperator(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 operatorStack.top == -1 {
			break
		}
		num1, _ = numStack.Pop()
		num2, _ = numStack.Pop()
		operator, _ = operatorStack.Pop()
		result = operatorStack.Calculate(num1, num2, operator)
		// 将计算结果重新入数栈
		numStack.Push(result)
	}

	// 如果我们的算法没有问题,表达式也是正确的,则结果就是numStack最后数
	res, _ := numStack.Pop()

	return res
}

func main() {
	exp1 := "3+2*6-2"
	res1 := StackCalculate(exp1)
	fmt.Println(res1) // 13

	exp2 := "321+21*6-21"
	res2 := StackCalculate(exp2)
	fmt.Println(res2) // 426
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值