golang数据结构与算法——栈

文章目录

  • 一 栈的基础知识
    • 1.1 栈的介绍
    • 1.2 栈的入栈和出栈的示意图
    • 1.3 栈的应用场景
  • 二 使用数组模拟栈
    • 2.1 入栈
    • 2.2 遍历
    • 2.3 出栈
  • 三 栈实现简单综合计算器


一 栈的基础知识

1.1 栈的介绍

有些程序员也把栈称为堆栈,即栈和堆栈是同一个概念

  1. 栈的英文为(stack)
  2. 栈是一个先入后出(FILO-First In Last Out)的有序列表
  3. 栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)。
  4. ​ 根据堆栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除

1.2 栈的入栈和出栈的示意图

golang数据结构与算法——栈_第1张图片

1.3 栈的应用场景

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

二 使用数组模拟栈

2.1 入栈

package main

import (
	"errors"
	"fmt"
)

// 用数组模拟栈的使用
type Stack struct {
	MaxTop int    // 表示栈最大可以存放的数据的个数
	Top    int    // 表示栈顶 栈顶是固定的,因此可以不用设置
	arr    [5]int // 数组模拟栈
}

// 入栈
func (s *Stack) Push(val int) (err error) {
	// 先判断栈是否已经满了
	if s.Top == s.MaxTop-1 {
		fmt.Println("栈已经满了!")
		return errors.New("stack full")
	}
	s.Top++
	// 放入数据
	s.arr[s.Top] = val
	return
}

func main() {
	stack := &Stack{
		MaxTop: 5,
		Top:    -1,
	}
	// 入栈
	stack.Push(1)
	stack.Push(2)
	stack.Push(3)
	stack.Push(4)
	stack.Push(5)
}

2.2 遍历

package main

import (
	"errors"
	"fmt"
)

// 用数组模拟栈的使用
type Stack struct {
	MaxTop int    // 表示栈最大可以存放的数据的个数
	Top    int    // 表示栈顶 栈顶是固定的,因此可以不用设置
	arr    [5]int // 数组模拟栈
}

// 入栈
func (s *Stack) Push(val int) (err error) {
	// 先判断栈是否已经满了
	if s.Top == s.MaxTop-1 {
		fmt.Println("栈已经满了!")
		return errors.New("stack full")
	}
	s.Top++
	// 放入数据
	s.arr[s.Top] = val
	return
}

// 遍历栈,需要从栈顶开始遍历
func (s *Stack) List() {
	// 判断是否为空栈
	if s.Top == -1 {
		fmt.Println("stack empty!")
		return
	}
	fmt.Println("栈的遍历输出结果如下:")
	for i := s.Top; i >= 0; i-- {
		fmt.Printf("arr[%d]=%d\n", i, s.arr[i])
	}
}

func main() {
	stack := &Stack{
		MaxTop: 5,
		Top:    -1,
	}
	// 入栈
	stack.Push(1)
	stack.Push(2)
	stack.Push(3)
	stack.Push(4)
	stack.Push(5)
	stack.Push(6)

	// 遍历
	stack.List()

}

运行结果:

[Running] go run "e:\golang开发学习\go_pro\test.go"
栈已经满了!
栈的遍历输出结果如下:
arr[4]=5
arr[3]=4
arr[2]=3
arr[1]=2
arr[0]=1

[Done] exited with code=0 in 1.266 seconds

2.3 出栈

package main

import (
	"errors"
	"fmt"
)

// 用数组模拟栈的使用
type Stack struct {
	MaxTop int    // 表示栈最大可以存放的数据的个数
	Top    int    // 表示栈顶 栈顶是固定的,因此可以不用设置
	arr    [5]int // 数组模拟栈
}

// 入栈
func (s *Stack) Push(val int) (err error) {
	// 先判断栈是否已经满了
	if s.Top == s.MaxTop-1 {
		fmt.Println("栈已经满了!")
		return errors.New("stack full")
	}
	s.Top++
	// 放入数据
	s.arr[s.Top] = val
	return
}

// 出栈
func (s *Stack) Pop() (val int, err error) {
	// 先判断栈是否为空
	if s.Top == -1 {
		fmt.Println("stack empty!")
		return 0, errors.New("stack empty")
	}

	// 先取值 再 s.Top
	val = s.arr[s.Top]
	s.Top--
	return val, nil
}

// 遍历栈,需要从栈顶开始遍历
func (s *Stack) List() {
	// 判断是否为空栈
	if s.Top == -1 {
		fmt.Println("stack empty!")
		return
	}
	fmt.Println("栈的遍历输出结果如下:")
	for i := s.Top; i >= 0; i-- {
		fmt.Printf("arr[%d]=%d\n", i, s.arr[i])
	}
}

func main() {
	stack := &Stack{
		MaxTop: 5,
		Top:    -1,
	}
	// 入栈
	stack.Push(1)
	stack.Push(2)
	stack.Push(3)
	stack.Push(4)
	stack.Push(5)

	// 遍历
	stack.List()

	// 出栈
	val, _ := stack.Pop()
	fmt.Println("出栈 val=", val)
	// 显示
	stack.List()

}

运行结果:

[Running] go run "e:\golang开发学习\go_pro\test.go"
栈的遍历输出结果如下:
arr[4]=5
arr[3]=4
arr[2]=3
arr[1]=2
arr[0]=1
出栈 val= 5
栈的遍历输出结果如下:
arr[3]=4
arr[2]=3
arr[1]=2
arr[0]=1

[Done] exited with code=0 in 1.251 seconds

三 栈实现简单综合计算器

思路分析

golang数据结构与算法——栈_第2张图片

代码实现

package main

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

// 用数组模拟栈的使用
type Stack struct {
	MaxTop int    // 表示栈最大可以存放的数据的个数
	Top    int    // 表示栈顶 栈顶是固定的,因此可以不用设置
	arr    [5]int // 数组模拟栈
}

// 入栈
func (s *Stack) Push(val int) (err error) {
	// 先判断栈是否已经满了
	if s.Top == s.MaxTop-1 {
		fmt.Println("栈已经满了!")
		return errors.New("stack full")
	}
	s.Top++
	// 放入数据
	s.arr[s.Top] = val
	return
}

// 出栈
func (s *Stack) Pop() (val int, err error) {
	// 先判断栈是否为空
	if s.Top == -1 {
		fmt.Println("stack empty!")
		return 0, errors.New("stack empty")
	}

	// 先取值 再 s.Top
	val = s.arr[s.Top]
	s.Top--
	return val, nil
}

// 遍历栈,需要从栈顶开始遍历
func (s *Stack) List() {
	// 判断是否为空栈
	if s.Top == -1 {
		fmt.Println("stack empty!")
		return
	}
	fmt.Println("栈的遍历输出结果如下:")
	for i := s.Top; i >= 0; i-- {
		fmt.Printf("arr[%d]=%d\n", i, s.arr[i])
	}
}

// 判断一个字符是否为运算符 这里只做 [+ - * /]
func (s *Stack) IsOper(val int) bool {
	// 这里通过ASCII值比较
	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 := "300+2*6-2"
	// 定义一个index帮助我们扫描表达式
	index := 0

	num1 := 0
	num2 := 0
	oper := 0
	result := 0
	keepNum := ""

	for {
		//取出字符串
		// 处理多位数的逻辑
		ch := exp[index : index+1]
		// 判断是否是数字或者是运算符号 ch==>"+"—==>43
		temp := int([]byte(ch)[0]) // 这个就是字符对应的ASCII值
		if operStack.IsOper(temp) {
			// 说明是符号
			// 空栈直接入栈
			if operStack.Top == -1 {
				operStack.Push(temp)
			} else {
				// 如果不是空栈 比较优先级

				//如果发现 opertStack 栈顶的运算符的优先级大于等于当前准备入栈的运算符的优先级,
				//就从符号栈 pop 出,并从数栈也 pop 两个数,进行运算,运算后的结果再重新入栈
				//到数栈, 当前符号再入符号栈
				if operStack.Priority(operStack.arr[operStack.Top]) >=
					operStack.Priority(temp) {
					num1, _ = numStack.Pop()
					num2, _ = numStack.Pop()
					oper, _ = operStack.Pop()
					// 注意下num的顺序即可
					result = operStack.Cal(num1, num2, oper)
					// 将计算结果 重新压入数栈中
					numStack.Push(result)
					// 将当前的符号压入符号栈
					operStack.Push(temp)
				} else {
					operStack.Push(temp)
				}
			}
		} else {
			// 这里不能直接压 因为它压的是 3的ASCII 51
			// 处理多位数的思路
			// 1. 先定义一个变量 keynum, 用来做拼接的
			// 2. 每次要向index后的字符 下探以为 判断是否运算符在做处理
			// 已经到最后一位 直接拼入就可以了
			keepNum += ch
			if index == len(exp)-1 {
				val, _ := strconv.ParseInt(keepNum, 10, 64)
				numStack.Push(int(val))
			} else {
				// 向index后面探测一位 后面一位是操作符
				if operStack.IsOper(int([]byte(exp[index+1 : index+2])[0])) {
					val, _ := strconv.ParseInt(keepNum, 10, 64)
					numStack.Push(int(val))
					keepNum = ""
				}
			}

		}
		// 继续扫描
		if index+1 == len(exp) {
			break
		}
		index++
	}
	//如果扫描表达式完毕,依次从符号栈取出符号,然后从数栈取出两个数,
	//运算后的结果,入数栈,直到符号栈为空
	for {
		if operStack.Top == -1 {
			// 符号栈为空 退出
			break
		}
		num1, _ = numStack.Pop()
		num2, _ = numStack.Pop()
		oper, _ = operStack.Pop()
		// 注意下num的顺序即可
		result = operStack.Cal(num1, num2, oper)
		// 将计算结果 重新压入数栈中
		numStack.Push(result)
	}
	// 如果我们的算法没有问题,表达式也是正确的,则结果就是numStack最后数
	res, _ := numStack.Pop()
	fmt.Printf("表达式%s= %v\n", exp, res)
}

运行结果:

[Running] go run "e:\golang开发学习\go_pro\test.go"
表达式300+2*6-2= 310

[Done] exited with code=0 in 1.41 seconds

你可能感兴趣的:(GO语言数据结构与算法,golang,算法)