IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> Go数据结构与算法之栈 -> 正文阅读

[数据结构与算法]Go数据结构与算法之栈

作者:template-box

概述

栈(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
}

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-08-14 14:21:25  更:2021-08-14 14:21:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 20:53:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码