栈的链式存储结构

相比栈的顺序存储的数组结构来说 , 我们采用 链式存储结构,存取更加灵活

栈的存储结构 无非就是数组 和链表 都加上了一个栈顶指针 , 然后来构造栈的结构

接下来 ,我们采用单链表来充当栈的存储结构 , 头结点比较方便定位,所以就让头结点的后继节点当栈顶了

定义存储结构:

typedef struct linknode
{
    //定义链表节点数据
    ElemType data;
    //定义指针域,单链表的后继指针域
    struct linknode *next; 
}LiStack;

优点 :不存在栈满上溢的情况, 单链表可以一直插入

栈的四要素:

●栈空的条件

        • s->next =   NULL; 


我们定义的是头结点的后继节点是栈顶节点,所以头结点的后继指针为空则,代表空栈

●栈满条件

        •不考虑

● 进栈操作

        • 将包含新数据 e 的链表新节点 插入到头结点之后

●退栈操作

         • 取出栈顶元素 ,并删除其链表栈顶节点,也就是删除链表的头结点的后继节点

初始化栈 initStack(&s)

●建立一个空栈 s

传入要建立的栈表

void InitStack(LiStack *&s){

为头节点分配空间 , 头结点的后继指针置空

    s = (ListStack *)malloc(sizeof(LiStack));
    s->next = NULL;
}

销毁栈 ClearStack(&s)

●释放栈 s 占用的全部存储空间

     其实就是链表的释放

传入要销毁的链表

void DestroyStack(LiStack *&s)
{

   从头结点开始 , 遍历销毁节点 ,但是销毁节点后 ,就会断开后继节点的联系 , 所以我们用一个指针 *p 指向要销毁的节点 ,另一个节点 *q 指向要联系删除的后续结点

    //定义要删除的指针 *p ,保存后续链表的节点指针 *q
    LiStack *p ;
    LiStack *q ;
    //初始的时候 , p 指向 头结点
    p = s;
    //保存删除的节点后的后续节点
    q = p->next;

    //下面开始,循环 free(p) ,直到 要删除的节点指针 p 的下一个节点是空 
    //这里 的 p->next != NULL 和 q != NULL  是一样的
    while( p->next != NULL)
    {
        //先释放指针 p所指向的节点
        free(p);
        //然后将 下一个节点 q 赋值给 接下来要删除的节点指针 p
        p = q;
        //保存接下来要删除的节点的下一个节点
        q = p->next;
    }
    //当 p 的下一个节点是空的时候 ,我们就跳出的 ,那我们就少 free 了尾结点 p
    free(p);
}

判断栈是否为空 StackEmpty(s)

        ●栈S为空的条件是 链表头结点的后继指针指向为空 ,即单链表中没有数据节点

e71dec4646a24504824f921cf6aaea11.png


返回相应的结果即可


 
bool StackEmpty(LiStack *s)
{
    return(s->next == NULL);
}

进栈Push(&s,e)

        ●将数据节点插入到头结点之后

f02bc54be7e94d68a911d339584f141c.png


进栈 ,就是把新节点插入到栈顶 ,也就是用头插法,插入到 头结点的后边 

传入要进的单链表栈 ,进栈的新元素

void Push(LiStack *&s,ElemType e)
{

构建新节点

//定义链表节点指针
LiStack *p;
//为新节点申请空间
p = (LiStack *)malloc(sizeof(LiStack));
//为新节点的数据区赋值
p -> data = e;
//头插法
//将新节点后继指针指向下一个节点 ,下一个节点插入前存放在头结点的后继指针
p -> next = s ->next;
//然后将头结点的后继指针指向新节点
s->next = p;

头插法两步,不能调换,因为我们在覆盖 s->next 的时候 ,要考虑到其存放有 下一个节点的地址,要先将其备份,或者处理后,再进行覆盖,

所以我们先去 利用 s->next ,将新节点的后继指针指向 下一个节点

出栈Pop(&s , & e)

        ●在栈不为空的条件下 ,将头结点后继数据节点的数据域赋给 e ,然后将其删除

175dd598c3274b4db9b19fd7df5f4be1.png


先判断链表是否为空 ,然后再将头结点的后继节点 ,也就是栈顶指针删除 ,并将其数据通过指针地址传出


//传入要出栈的链栈 , 传入要出栈的指针元素
bool Pop(LiStack *&s , ElemType &e)
{
	//用指针指向要出栈的元素,就是头结点的后继节点
	LiStack *p;
	//如果s->next 为空,则表明为空栈 ,无法出栈元素
	if(s->next == NULL)
	{
		return false;
	}
	//如果经过上面条件的验证,s->next不为空,则接着往下走
	//先定位要出栈的元素
	p = s->next;	//头结点的后继节点
	e = p->data;	//栈顶数据传出
	//开始删除头结点的后继节点 p,也就是头结点指向要删除节点的下一个节点
	s->next = p->next;
	//然后释放 p 空间
	free(p);
	return true;
}

取栈顶元素 GetTop(s,e)

        ●在栈不为空的条件下 ,将头节点后继节点的数据域赋给 e

6b659b4779044d9b8aebbe538f6bce37.png


取栈顶元素 和 出栈 的区别是:

取栈顶元素 , 就是如果栈顶指针不为空 , 就把所指的节点数据传出 , 不涉及节点的增删  

出栈不仅传出节点 ,而且要把节点删除 , 释放空间 ,多了一步结点连接


//传入要取元素的链栈 ,承载传出的栈顶元素的指针
bool GetTop(LiStack *s , ElemType &e)
{
	//先判断栈是否为空,标志就是栈顶是否为空,栈顶就是头结点的后继节点
	if(s->next == NULL)
	{
		//为空,则进入返回错误
		return false;
	}
	//不为空,则继续进行,传出栈顶元素,返回正确
	e = s->next->data;
	return true;
}

 

 

 

 

 

 

 

  • 8
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值