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语言学习笔记 — 基础 — 基本语法 — 常量与变量 — 变量的生命周期:栈(stack)

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

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

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

Go语言数据结构与算法—常用排序算法