Go语言数据结构与算法—栈
Posted 小圣.
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言数据结构与算法—栈相关的知识,希望对你有一定的参考价值。
概述
栈(stack)是一种先进后出(First In Last Out, FILO)的特殊线性表,其插入和删除操作只允许在线性表的一段进行。允许操作的一端称为栈顶(top),不允许操作的一端称为栈底(bottom)。栈中插入元素的操作称为入栈(push),删除元素的操作称为出栈(pop)。
常用的应用场景:
- 子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中
- 处理递归调用:和子程序的调用类似,只是除了存储下一个指令的地址外,也将参数、区域变量等数据存入到堆栈中
- 表达式的转换与求值
- 二叉树的遍历
- 图形的深度优先搜索算法
顺序栈
type Stack struct {
// 栈的容量
MaxTop int
// 栈堆
Top int
// 数组模拟栈
Array [5]int
}
func (s *Stack) Push(val int) {
// fmt.Println(len(s.Array))
// 判断栈是否满了
if s.Top >= s.MaxTop-1 {
fmt.Println("the stack is full")
return
}
s.Top++
// 入栈
s.Array[s.Top] = val
return
}
// 出栈
func (s *Stack) Pop() (data int) {
// 判断栈是否为空
if s.Top == -1 {
fmt.Println("the stack is empty")
return
}
data = s.Array[s.Top]
s.Top--
return
}
// 遍历栈
func (s *Stack) List() {
// 判断栈是否满了
if s.Top == -1 {
fmt.Println("the stack is empty")
return
}
for i := s.Top; i >= 0; i-- {
fmt.Println(s.Array[i])
}
}
链式栈
// 栈节点
type Node struct {
data int
next *Node
}
// 栈链表
type Stack struct {
maxLength int
length int
head *Node
}
// 初始化一个栈
func InitStack(maxLength int) *Stack {
return &Stack{
maxLength: maxLength,
length: 0,
head: nil,
}
}
// 入栈
func (s *Stack) Push(val int) {
// 判断栈是否满了
if s.length >= s.maxLength {
fmt.Println("the stack is full")
return
}
// 生成要入栈的节点
node := &Node{
data: val,
next: s.head,
}
// 入栈
s.head = node
s.length++
}
// 取出一个元素
func (s *Stack) Pop() (data int, err error) {
if s.length <= 0 {
err = errors.New("the stack is empty")
return
}
data = s.head.data
// 指向后面的一个节点
s.head = s.head.next
s.length--
return
}
// 查看所有元素
func (s *Stack) List() {
// 判断是否为空
if s.length == 0 {
fmt.Println("the stack is empty")
return
}
temp := s.head
for i := 0; i < s.length; i++ {
fmt.Println(temp.data)
temp = temp.next
}
}
求算术表达式
type Stack struct {
// 栈的容量
MaxTop int
// 栈堆
Top int
// 数组模拟栈
Array [20]int
}
func (s *Stack) Push(val int) {
// fmt.Println(len(s.Array))
// 判断栈是否满了
if s.Top >= s.MaxTop-1 {
fmt.Println("the stack is full")
return
}
s.Top++
// 入栈
s.Array[s.Top] = val
return
}
// 出栈
func (s *Stack) Pop() (data int) {
// 判断栈是否为空
if s.Top == -1 {
fmt.Println("the stack is empty")
return
}
data = s.Array[s.Top]
s.Top--
return
}
// 遍历栈
func (s *Stack) List() {
// 判断栈是否满了
if s.Top == -1 {
fmt.Println("the stack is empty")
return
}
for i := s.Top; i >= 0; i-- {
fmt.Println(s.Array[i])
}
}
// 判断一个字符是不是运算符[+ - * /]
func (s *Stack) IsOper(val int) bool {
// 这些数字是加减乘除对于的ASSIC码
if val == 42 || val == 43 || val == 45 || val == 47 {
return true
} else {
return false
}
}
// 运算的方法
func (s *Stack) Cal(num1 int, num2 int, 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 *Stack) Priority(oper int) int {
res := 0
if oper == 42 || oper == 47 {
res = 1
} else if oper == 43 || oper == 45 {
res = 0
}
return res
}
func main() {
// 数栈
numStack := &Stack{
MaxTop: 20,
Top: -1,
}
// 符号栈
operStack := &Stack{
MaxTop: 20,
Top: -1,
}
// 表达式
exp := "3+2*6-2"
// 定义一个index, 帮助扫描exp
index := 0
// 辅助变量
num1 := 0
num2 := 0
oper := 0
result := 0
keepNum := ""
for {
// 循环遍历每一个字符
ch := exp[index : index+1]
// 字符转化为ASSIC码
temp := int([]byte(ch)[0])
if operStack.IsOper(temp) {
// 说明是符号
// 如果是一个空栈,直接入栈
if operStack.Top == -1 {
operStack.Push(temp)
} else {
if operStack.Priority(operStack.Array[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 后面测试看看是不是运算符 [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)
}
// 此时栈中的最后一个数就是结果
res := numStack.Pop()
fmt.Printf("表达式%s = %v", exp, res)
}
最后
完整代码:https://github.com/bigzoro/go_algorithm/tree/main/stack
以上是关于Go语言数据结构与算法—栈的主要内容,如果未能解决你的问题,请参考以下文章
go语言学习笔记 — 基础 — 基本语法 — 常量与变量 — 变量的生命周期:变量逃逸分析 —— go编译器自动决定变量的内存分配方式(堆还是栈),提高程序运行效率