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编译器自动决定变量的内存分配方式(堆还是栈),提高程序运行效率

[译] Go语言使用TCP_NODELAY控制发包流量

go语言学习笔记 — 基础 — 基本语法 — 常量与变量 — 变量的生命周期:栈(stack)

数据结构与算法栈与队列C语言版

云原生训练营模块一 Go语言特性

五种编程语言解释数据结构与算法—链式栈