Golang实现栈结构

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

栈结构图

常用方法

  1. Peek():返回栈顶元素
  2. Pop():弹出栈顶元素
  3. Push():将元素入栈
  4. Size():栈的元素个数
  5. IsEmpty():栈是否为空

实现

根据栈结构定义,我们发现主要需要解决如下问题

维护一个栈顶元素
栈顶元素需要和前一个元素关联
栈元素个数

根据上述我们可以使用指针属性来关联或者使用切片这种物理关联

使用指针

// 基础结构体 使用泛型实现
type stack[T any] struct {
	top  *node[T] // 栈顶元素
	size int // 栈的元素个数
}

// 栈内元素,包括值、和前一元素的地址
type node[T any] struct {
	val T
	pre *node[T]
}

// 构造方法
func NewStack[T any]() *stack[T] {
	return &stack[T]{
		top:  nil,
		size: 0,
	}
}

// 入栈
func (s *stack[T]) Push(val T) {
	// 创建新元素节点,新元素的前一节点是当前栈的栈顶元素节点
	newNode := &node[T]{val: val, pre: s.top}
	// 修改栈的栈顶元素为新元素节点
	s.top = newNode
	// 修改栈的大小
	s.size++
}

// 弹栈, 返回参数可以不包含ok
func (s *stack[T]) Pop() (val T, ok bool) {
	// 判断栈的栈顶元素是否为空,或者判断元素个数是否为0
	if s.top == nil {
		return
	}
	// 取值
	val = s.top.val
	// 栈顶元素移动
	s.top = s.top.pre
	ok = true
	// 大小减少
	s.size--
	return
}
// 获取栈顶元素
func (s *stack[T]) Peek() (val T, ok bool) {
	if s.top == nil {
		return
	}
	val, ok = s.top.val, true
	return
}

func (s *stack[T]) Size() int {
	return s.size
}

func (s *stack[T]) IsEmpty() bool {
	return s.size == 0
}

使用切片

type stack[T any] struct {
	slice []T // 栈元素队列
	index int // 栈的顶元素的索引
}

// 构造方法
func NewStack[T any]() *stack[T] {
	return &stack[T]{
		top:  nil,
		index: -1,
	}
}

// 入栈,由于是切片会发生扩容
func (s *stack[T]) Push(val T) {
	s.slice = append(s.slice, val)
	s.index++
}

// 弹栈, 返回参数可以不包含ok
func (s *stack[T]) Pop() (val T, ok bool) {
	// 判断栈顶元素的索引是否为-1
	if s.index == -1{
		return
	}
	val = s.slice[s.index]
	s.slice = s.slice[:index]
	s.index--
	return
}
// 获取栈顶元素
func (s *stack[T]) Peek() (val T, ok bool) {
	// 判断栈顶元素的索引是否为-1
	if s.index == -1{
		return
	}
	val, ok = s.slice[s.index], true
	return
}

func (s *stack[T]) Size() int {
	return len(s.slice)
}

func (s *stack[T]) IsEmpty() bool {
	return s.index == -1
}

对比

对比发现,使用切片代码简单,但是由于会发生扩容,所以性能相对于链表来说不是很好,但是如果我们可以确定栈的容量,那么我们就可以考虑使用切片的栈

切片栈的简单实现

// 直接初始化
stack := make([]int, n)
// 栈顶元素索引,-1代表没有
index := -1
// 入栈 Push(val T)
stack[index+1]=val
index++
// 弹栈 Pop()
val = stack[index]
index--
// 获取栈顶元素 Peek()
val = stack[index]
// Size()
size = index+1
// IsEmpty() 
index == -1

golang自带实现

type name []type
	
func (s *name) Push(item type) {
	*s = append(*s, item)
}

func (s *name) Pop() (item type) {
	if len(*s) == 0 {
		return
	}

	*s, item = (*s)[:len(*s)-1], (*s)[len(*s)-1]
	return item
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Golang 提供了一些底层数据结构,这些数据结构可以用于构建高效的程序。以下是一些常见的底层数据结构: 1. 数组(Arrays):在 Golang 中,数组是固定长度的数据结构,可以存储相同类型的元素。数组使用索引访问元素,具有快速的随机访问能力。 2. 切片(Slices):切片是一个动态长度的数组,可以根据需要进行扩展或收缩。切片是基于数组实现的,提供了更灵活的操作和更方便的使用。 3. 映射(Maps):映射是一种无序的键值对集合。它类似于字典或哈希表,通过键来访问值。Golang 的映射使用哈希表来实现,具有快速的查找和插入能力。 4. 链表(Linked Lists):链表是一种基本的数据结构,它由多个节点组成,每个节点包含一个值和一个指向下一个节点的指针。链表可以用于实现队列、和其他高级数据结构。 5. (Stacks):是一种后进先出(LIFO)的数据结构,只能在顶进行插入和删除操作。Golang 中可以使用切片或链表实现。 6. 队列(Queues):队列是一种先进先出(FIFO)的数据结构,只能在队尾进行插入操作,在队头进行删除操作。Golang 中可以使用切片或链表实现队列。 7. 堆(Heaps):堆是一种特殊的二叉树,具有一些特定的性质。在 Golang 中,可以使用堆接口和堆包来实现最小堆或最大堆。 8. 树(Trees):树是一种非线性数据结构,由节点和边组成。树在计算机科学中有广泛的应用,如二叉树、AVL 树、红黑树等。 这些底层数据结构可以帮助开发者构建高效的程序,并在不同的应用场景中发挥作用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值