栈简单入门

247 阅读3分钟

栈(Stack)是一种常见的数据结构,它遵循“后进先出”(Last-In-First-Out,LIFO)的原则。最后被加入栈中的元素,第一个被取出来。栈有两个基本操作:压入(push)和弹出(pop)。压入将一个元素添加到栈的顶部,弹出则将顶部元素移除并返回它。

定义和基本操作

栈是一组元素的集合,可以进行插入和删除操作。栈中的插入和删除操作只能在栈顶进行。栈的基本操作包括:

  • 压入(push):将一个元素添加到栈的顶部。
  • 弹出(pop):移除栈顶元素并返回它。
  • 查看栈顶元素(top/peek):返回栈顶元素,但不移除它。
  • 判断栈是否为空(isEmpty):返回栈是否为空的布尔值。
  • 获取栈的大小(size):返回栈中元素的数量。

实现方式

栈的实现方式有两种:顺序栈和链式栈。

顺序栈是使用数组实现的栈,可以随机访问栈中的元素。顺序栈的插入和删除操作都在栈顶进行。链式栈则使用链表实现,可以动态地分配和释放内存。

由于我见到的大部分情况都是使用数组实现的栈,这里仅给出使用数组来实现栈的代码:

 package main
 ​
 import "fmt"
 ​
 // IntSeqStack 结构体,包含一个整型切片和一个整型变量 top,用于实现顺序栈
 type IntSeqStack struct {
     data []int // 整型切片,用于存储栈中的元素
     top  int   // 整型变量,用于记录栈顶元素的下标
 }
 ​
 // NewIntSeqStack 函数用于创建一个 IntSeqStack 实例,并返回其指针
 func NewIntSeqStack() *IntSeqStack {
     return &IntSeqStack{make([]int, 0), -1} // 使用 make 函数创建一个空的整型切片,top 初始值为 -1
 }
 ​
 // Push 函数用于向栈中添加一个整型元素
 func (s *IntSeqStack) Push(item int) {
     s.top++ // 栈顶下标加 1
     if s.top < len(s.data) { // 如果栈顶下标小于栈中元素的数量
         s.data[s.top] = item // 直接将元素放到栈顶位置
     } else { // 如果栈顶下标大于等于栈中元素的数量
         s.data = append(s.data, item) // 在栈中添加一个新的元素
     }
 }
 ​
 // Pop 函数用于从栈中删除一个整型元素,并返回其值
 func (s *IntSeqStack) Pop() int {
     if s.top == -1 { // 如果栈为空,返回 0
         return 0
     }
     item := s.data[s.top] // 获取栈顶元素的值
     s.top-- // 栈顶下标减 1
     return item // 返回栈顶元素的值
 }
 ​
 // Peek 函数用于返回栈顶元素的值但不删除它
 func (s *IntSeqStack) Peek() int {
     if s.top == -1 { // 如果栈为空,返回 0
         return 0
     }
     return s.data[s.top] // 返回栈顶元素的值
 }
 ​
 // IsEmpty 函数用于判断栈是否为空,返回布尔值
 func (s *IntSeqStack) IsEmpty() bool {
     return s.top == -1 // 如果栈顶下标为 -1,说明栈为空,返回 true,否则返回 false
 }
 ​
 // Size 函数用于返回栈中元素的数量
 func (s *IntSeqStack) Size() int {
     return s.top + 1 // 栈中元素的数量等于栈顶下标加 1
 }
 ​
 func main() {
     s := NewIntSeqStack()
     s.Push(1)
     s.Push(2)
     s.Push(3)
     fmt.Println(s.Pop())     // 3
     fmt.Println(s.Peek())    // 2
     fmt.Println(s.IsEmpty()) // false
     fmt.Println(s.Size())    // 2
 }

这里栈顶元素使用了 -1 作为栈空的标识位。

应用场景

栈在计算机程序中有广泛的应用场景,例如:

  • 函数调用栈:当一个函数被调用时,函数的参数和局部变量都被压入调用栈中。当函数返回时,栈顶元素(即最后被压入栈中的元素)被弹出,控制权返回到调用函数的位置。
  • 表达式求值:操作数和运算符按照一定的顺序被压入栈中,然后根据运算符的优先级和结合性进行计算。
  • 括号匹配:当遇到左括号时,将其压入栈中,当遇到右括号时,弹出栈顶元素并检查它是否匹配左括号。
  • 浏览器的前进后退功能:当浏览器访问新页面时,将当前页面压入栈中,当用户点击“后退”按钮时,弹出栈顶元素并返回上一个页面。

除了上述应用场景,栈还可以拓展为其他数据结构,例如双端栈和循环栈等。