Go语言 大话数据结构——栈的链式存储结构

链式存储结构最大的好处就是没有空间的限制,通过指针指向将结点像一个链子一样把结点链接,那么栈的同样可以用于链式存储结构。

栈的链式存储结构,简称为链栈。想想看,栈只是栈顶来做插入和删除操作,栈顶放在链表的头部还是尾部呢?由于单链表有头指针,而栈顶指针也是必须的,那么干嘛不让他们合二为一呢,所以比较好的办法是把栈顶放到单链表的头部。另外栈顶在头部了,那么单链表的头结点也就失去了意义,通常对于链栈来说,是不需要头结点的。

同样对于链栈来说,基本不存在栈满的情况,除非内存已经没有可用的空间了。

下面是链栈的指向图:

Go语言 大话数据结构——栈的链式存储结构_第1张图片

同样链栈中最重要的算法是压栈和弹栈。

压栈操作:

 

 

操作很简单,新建结点,给结点数据域赋值,指针域指向原来栈顶元素。

然后将栈顶指针指向新结点,元素个数+1,就ok。如下图:

Go语言 大话数据结构——栈的链式存储结构_第2张图片

弹栈操作:

 

和压栈相反,将栈顶元素的数据域取通过指针返回这个数据,将要弹出的元素的指针域赋值给栈顶指针,然后释放这个元素,这样栈顶指针就指向新的栈顶了,然后元素个-1。如图:

Go语言 大话数据结构——栈的链式存储结构_第3张图片

下面我们来看一下相关代码的实现。

代码实例:

package main

import (
	"errors"
	"fmt"
)

type seleType int
//节点数据类型
type StackNode struct {
	data seleType//数据
	next *StackNode//指针
}
type LinkStackPtr *StackNode
type LinkStak struct {
	top   LinkStackPtr//栈顶指针,每次都操作栈顶指针,为节点类型的指针
	count int  //保存节点的数量
}

//入栈操作
func push1(s *LinkStak, e seleType) {
	p := LinkStackPtr(new(StackNode)) //新分配内存空间
	p.data = e
	p.next = s.top //新添加的数据的指针指向原来栈顶的元素
	s.top = p      //调整栈顶
	s.count++      //调整数量
}

//出栈操作
func pop1(s *LinkStak) (err error, res seleType) {
	if s.top == nil {
		err = errors.New("栈为空,不能出栈")
		return
	}
	res=s.top.data
	p:=s.top
	s.top=p.next
	s.count--
	return

}
func main() {
	var link LinkStak
	push1(&link,123)
	push1(&link,456)
    fmt.Println("链表数据节点:",link.count)
	_,res:=pop1(&link)
	fmt.Println(res)
	_,res=pop1(&link)
	fmt.Println(res)
	err,res:=pop1(&link)
	fmt.Println(err,res)
	fmt.Println("链表数据节点:",link.count)

}

运行效果:

Go语言 大话数据结构——栈的链式存储结构_第4张图片

注:栈的顺序存储结构可以理解为:通过结构体对数组进行操作;栈的链式存储结构可以理解为:通过结构体对链表进行操作。

你可能感兴趣的:(Go语言 大话数据结构——栈的链式存储结构)