我的Go+语言初体验——GO+实现数据结构之【栈与其应用】(2)

简介: 我的Go+语言初体验——GO+实现数据结构之【栈与其应用】(2)

什么是栈

关于什么是栈, 我们可以先看百度百科给的解释


栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。


或许你现在很迷茫? 其实很好理解, 我们先来理解一些基础的概念


你可以把栈理解为一个这样的一水杯

1.png


此时这个水杯里面没有水,这种情况对于栈来说叫做空栈


那么此时我们来倒入一些水


2.png

我们平时接水时是不是先进入杯子的在底部, 而后进入的则在上面 ( 请暂时忽略掉水作为液体的特性 )


我们接水的动作,对于栈来说则被称为入栈


而倒水的动作被,对于栈来说则被称为出栈


就像水杯里面的水一样, 栈内数据遵循一个规则,那就是先进后出,后进先出, 在栈里面可不讲究先来后到


那如果杯子里的水溢出来了呢?


3.png


对于栈来说这就被叫做栈溢出


如果接的是热水,那是不是就烫到手了; 而栈在计算机里也是,溢出了也是要报错的,那如何防止出现这种情况呢?


我们可以通过一个概念来进行判断, 那就是栈是否栈满


接下来,我们来小结一下, 栈的基本概念/状态


  • 栈内数据遵循: 先进后出, 后进先出规则
  • 栈内添加元素: 入栈
  • 栈内取出元素: 出栈
  • 栈内元素满了: 满栈
  • 栈内没有元素: 空栈
  • 栈内元素溢出: 栈溢出

不必去记住这些术语, 主要理解栈内的基本数据流程



GO+ 实现栈


终于要动手了, 现在我们来实现出栈 的数据结构.


要设计栈, 我们需要需要有一个变量才存储栈内的元素.


我们先设计出基本的变量来, 为了防止栈溢出所以我们需要一个变量来限制栈的元素容量.


遵守先进后出, 后进先出的规则, 我们操作都需要对最顶部的元素进行操作, 所以我们还需要一个变量来标记栈顶元素的索引.


分析到这里,栈的数据结构已经呼之欲出了。我们就直接写出栈的数据结构和他的构造方法。


TIps

& 是取地址符号 , 即取得某个变量的地址 , 如: &a

* 是指针运算符 , 表示一个变量是指针类型 , 也可表示指针变量所指向的存储单元 , 也就是这个地址所存储的值 .

/ 定义栈的结构体
type Stack struct {
  // 用装元素的切片
  stackSet []string
  // 栈顶位置标记
  stackTop int
  // 栈的容量
  stackSize int
}
// 初始化栈
func NewStack(size int) *Stack {
  // 返回栈的初始值
  return &Stack{
  stackSet:  make([]string, size),
  stackTop:  0,
  stackSize: size,
  }
}

栈顶指针初始可以指向0, 也可以指向-1


你可以这样理解-1: 当第一个元素入栈的时候, 栈顶指针就可以通过自加指向0元素,从而避免其它判断.


栈的基本操作

介绍

当我们了解了栈的基本概念之后, 接下来就简单多了


对于一个栈来说,如果我们将基本概念融入到代码之中, 其基本操作分为以下四种


  • 入栈 : 存入数据
  • 出栈 : 取出数据
  • 满栈判断 : 避免栈溢出
  • 空栈判断 : 避免栈空了以后还有出栈的请求


接下来我们准备四个方法来进行实现


入栈

// 功能: 元素入栈
// 返回: 入栈成功返回true,返之返回false
func (s *Stack) Push(e string) bool {
  // 判断栈是否为满栈
  if s.IsFull() {
  return false
  }
  // 在栈的顶部添加数据
  s.stackSet[s.stackTop] = e
  // 重新定位栈顶的位置
  s.stackTop++
  return true
}

出栈

// 功能: 元素出栈
// 返回: 出栈成功返回true + 原始栈, 返之返回false + 取出之后的新栈
func (s *Stack) Pop() (flag bool, ret string) {
  // 判断是否为空栈
  if s.IsEmpty() {
  return false, ret
  }
  // 取出元素
  ret = s.stackSet[s.stackTop-1]
  // 重新定位栈顶的位置
  s.stackTop--
  return true, ret
}

判断是否空栈

// 功能: 判断栈是否为空
// 返回: 空栈返回true,返之返回false
func (s *Stack) IsEmpty() bool {
    // 直接判断栈顶元素即可
  if s.stackTop == 0 {
  return true
  }
  return false
}

判断是否满栈

// 功能: 判断栈是否为空
// 返回: 满栈返回true,返之返回false
func (s *Stack) IsFull() bool {
    // 判断栈顶位置是否和最大容量一样
  if s.stackTop == s.stackSize {
  return true
  }
  return false
}

案例


实现主函数

func main() {
  // 初始化栈
  stack := NewStack(3)
  println "\n初始的栈:", stack
  // 先测试下空栈是否能删除元素
  println stack.Pop()
  stack.Push("您好,我是uiu")
  stack.Push("我的博客链接: uiuing.com")
  println "\n元素入栈之后:", stack, "\n"
  // 删除掉刚刚的两天数据试试
  println stack.Pop()
  println stack.Pop()
  println "\n元素出栈之后:", stack
}


完整代码

package main
// 定义栈的结构体
type Stack struct {
  // 用装元素的切片
  stackSet []string
  // 栈顶位置标记
  stackTop int
  // 栈的容量
  stackSize int
}
// 初始化栈
func NewStack(size int) *Stack {
  // 返回栈的初始值
  return &Stack{
  stackSet:  make([]string, size),
  stackTop:  0,
  stackSize: size,
  }
}
// 功能: 元素入栈
// 返回: 入栈成功返回true,返之返回false
func (s *Stack) Push(e string) bool {
  // 判断栈是否为满栈
  if s.IsFull() {
  return false
  }
  // 在栈的顶部添加数据
  s.stackSet[s.stackTop] = e
  // 重新定位栈顶的位置
  s.stackTop++
  return true
}
// 功能: 元素出栈
// 返回: 出栈成功返回true + 原始栈, 返之返回false + 取出之后的新栈
func (s *Stack) Pop() (flag bool, ret string) {
  // 判断是否为空栈
  if s.IsEmpty() {
  return false, ret
  }
  // 取出元素
  ret = s.stackSet[s.stackTop-1]
  // 重新定位栈顶的位置
  s.stackTop--
  return true, ret
}
// 功能: 判断栈是否为空
// 返回: 空栈返回true,返之返回false
func (s *Stack) IsEmpty() bool {
  // 直接判断栈顶元素即可
  if s.stackTop == 0 {
  return true
  }
  return false
}
// 功能: 判断栈是否为空
// 返回: 满栈返回true,返之返回false
func (s *Stack) IsFull() bool {
  // 判断栈顶位置是否和最大容量一样
  if s.stackTop == s.stackSize {
  return true
  }
  return false
}
func main() {
  // 初始化栈
  stack := NewStack(3)
  println "\n初始的栈:", stack
  // 先测试下空栈是否能删除元素
  println stack.Pop()
  stack.Push("您好,我是uiu")
  stack.Push("我的博客链接: uiuing.com")
  println "\n元素入栈之后:", stack, "\n"
  // 删除掉刚刚的两天数据试试
  println stack.Pop()
  println stack.Pop()
  println "\n元素出栈之后:", stack
}

运行结果


4.png


如果还不清楚栈的实现, 请认真看完整代码的注释

最后,留给大家一道思考题,根据栈的特性,其有何处可以应用?


目录
相关文章
|
5月前
|
编译器 Go
揭秘 Go 语言中空结构体的强大用法
Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。
|
5月前
|
运维 监控 算法
监控局域网其他电脑:Go 语言迪杰斯特拉算法的高效应用
在信息化时代,监控局域网成为网络管理与安全防护的关键需求。本文探讨了迪杰斯特拉(Dijkstra)算法在监控局域网中的应用,通过计算最短路径优化数据传输和故障检测。文中提供了使用Go语言实现的代码例程,展示了如何高效地进行网络监控,确保局域网的稳定运行和数据安全。迪杰斯特拉算法能减少传输延迟和带宽消耗,及时发现并处理网络故障,适用于复杂网络环境下的管理和维护。
|
19天前
|
JSON 编解码 API
Go语言网络编程:使用 net/http 构建 RESTful API
本章介绍如何使用 Go 语言的 `net/http` 标准库构建 RESTful API。内容涵盖 RESTful API 的基本概念及规范,包括 GET、POST、PUT 和 DELETE 方法的实现。通过定义用户数据结构和模拟数据库,逐步实现获取用户列表、创建用户、更新用户、删除用户的 HTTP 路由处理函数。同时提供辅助函数用于路径参数解析,并展示如何设置路由器启动服务。最后通过 curl 或 Postman 测试接口功能。章节总结了路由分发、JSON 编解码、方法区分、并发安全管理和路径参数解析等关键点,为更复杂需求推荐第三方框架如 Gin、Echo 和 Chi。
|
2月前
|
分布式计算 Go C++
初探Go语言RPC编程手法
总的来说,Go语言的RPC编程是一种强大的工具,让分布式计算变得简单如同本地计算。如果你还没有试过,不妨挑战一下这个新的编程领域,你可能会发现新的世界。
59 10
|
1月前
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
21 0
栈区的非法访问导致的死循环(x64)
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。
|
5月前
|
存储 Go
Go 语言入门指南:切片
Golang中的切片(Slice)是基于数组的动态序列,支持变长操作。它由指针、长度和容量三部分组成,底层引用一个连续的数组片段。切片提供灵活的增减元素功能,语法形式为`[]T`,其中T为元素类型。相比固定长度的数组,切片更常用,允许动态调整大小,并且多个切片可以共享同一底层数组。通过内置的`make`函数可创建指定长度和容量的切片。需要注意的是,切片不能直接比较,只能与`nil`比较,且空切片的长度为0。
124 3
Go 语言入门指南:切片
|
5月前
|
算法 安全 Go
公司局域网管理系统里的 Go 语言 Bloom Filter 算法,太值得深挖了
本文探讨了如何利用 Go 语言中的 Bloom Filter 算法提升公司局域网管理系统的性能。Bloom Filter 是一种高效的空间节省型数据结构,适用于快速判断元素是否存在于集合中。文中通过具体代码示例展示了如何在 Go 中实现 Bloom Filter,并应用于局域网的 IP 访问控制,显著提高系统响应速度和安全性。随着网络规模扩大和技术进步,持续优化算法和结合其他安全技术将是企业维持网络竞争力的关键。
102 2
公司局域网管理系统里的 Go 语言 Bloom Filter 算法,太值得深挖了
|
5月前
|
算法 调度 C++
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
78 11
|
5月前
|
开发框架 前端开发 Go
eino — 基于go语言的大模型应用开发框架(二)
本文介绍了如何使用Eino框架实现一个基本的LLM(大语言模型)应用。Eino中的`ChatModel`接口提供了与不同大模型服务(如OpenAI、Ollama等)交互的统一方式,支持生成完整响应、流式响应和绑定工具等功能。`Generate`方法用于生成完整的模型响应,`Stream`方法以流式方式返回结果,`BindTools`方法为模型绑定工具。此外,还介绍了通过`Option`模式配置模型参数及模板功能,支持基于前端和用户自定义的角色及Prompt。目前主要聚焦于`ChatModel`的`Generate`方法,后续将继续深入学习。
712 7