数据结构(C语言版)——3.2、链式栈实现

3.2、链式栈实现

3.2.1、链式栈说明

  • 和单链表类似。链式栈即用一组任意的存储单元存储栈的数据元素。每个结点包括存放数据元素的数据域,和指示下一个栈结点的指针域。
  • 对于栈底元素而言,由于其没有下一个结点,故栈底结点的指针域始终为 NULL ,其数据域存放进栈的第一个数据元素。
  • 对于栈顶指针,由于栈只能通过栈顶进出数据,故只需要设置一个栈顶指针用于指示栈顶结点即可。链式栈的图解如下图所示。
    在这里插入图片描述
    注:可将链式栈结构看成不带头结点的单链表,将栈顶指针看成头指针,将单链表的头插操作看作入栈,头删看作出栈,而不能对尾部结点进行删插操作。这样易于理解。

3.2.2、链式栈的优缺点及适用场合

优点(相对顺序栈):

  • 插入删除操作方便,只需修改相邻结点的指向即可、存储空间动态,不存在溢出与浪费

缺点:

  • 需要额外空间来存储每个结点的指向关系、存储单元碎片化,空间利用率不高、查询代价较高,必须从头开始查找

适用情况:

  • 应用时,优先考虑顺序栈,因为顺序栈不像顺序队列那么浪费空间,同时顺序栈操作简单,故优先使用顺序栈。若用户无法预估所用栈的最大长度,则宜采用链式栈

3.2.3、存储结构定义及头文件声明

(1)存储结构定义

  • 根据上述分析,栈结点包括数据域、指针域,以及一个指示栈顶结点的栈指针(这里同时用该变量指示栈是否存在)。故,定义如下。
typedef struct SNode //链栈结点定义
{
   
    ElemType data;
    struct SNode *next; //后续结点指针
} SNode;
typedef SNode *LinkStack; //指向栈结点的指针(类似于单链表的头指针),用于指示栈顶位置

(2)头文件声明

  • 头文件声明包括函数结果状态代码 OK、ERROR,数据元素类型 ElemType ,栈的基本操作的函数原型声明,以及需要的库。

注:由于C语言不存在引用,需用传址。这里 LinkStack 虽然是指针类型,但是当改变栈的内容,是改变栈顶指针的指向时,需要传递栈顶指针的地址,也就是二级指针。

#ifndef LINKSTACK_H_
#define LINKSTACK_H_

#define OK 1
#define ERROR 0

typedef int Status;   //自定义函数类型,其值是函数结果的状态代码
typedef int ElemType; //自定义数据类型
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

typedef struct SNode //链栈结点定义
{
   
    ElemType data;
    struct SNode *next; //后续结点指针
} SNode;
typedef SNode *LinkStack; //指向栈结点的指针(类似于单链表的头指针),用于指示栈顶位置,看做全局变量
//链栈基本操作的函数原型
Status InitStack(LinkStack *S);//二级指针,S本身是一个指针,该函数要更改指针的指向,所以要传二级指针
//构造一个空栈 S
Status DestoryStack(LinkStack *S);
//销毁栈 S。结果使 S 指向NULL,所有结点均释放
Status ClearStack(LinkStack *S);
//将栈 S 置空,结果仅存在 S 的头结点,指向栈底元素,其数据域为空,指针域为NULL
bool StackEmpty(LinkStack S);
//判断栈 S 是否为空
int StackLength(LinkStack S);
//返回栈 S 的元素个数,即栈的长度
Status GetTop(LinkStack S, ElemType *e);
//获取 S 的栈顶元素
Status Push(LinkStack *S, ElemType e);
//插入元素 e 为新的栈顶元素
Status Pop(LinkStack *S, ElemType *e);
//删除栈顶元素,并用 e 返回其值
Status StackTraverse(LinkStack S /*,Status (*visit)()*/);
//从栈底到栈顶依次对栈中元素调用函数 visit()。
#endif

3.2.4、主要操作实现与思路

(1)初始化 InitStack()

  • 由于链式栈类似于不带头结点的单链表,所以初始化操作仅需初始化栈顶指针为空即可。
  • 这里的形参(LinkStack *S)是一个指向栈顶指针类型的指针,其赋空,是将栈顶指针赋空,故是 *S = NULL。
Status InitStack(LinkStack *S)
{
   //构造一个空栈 
    *S = NULL;   //即只有栈顶指针,即类似于单链表的头指针
    return OK;
}

(2)置空栈 ClearStack()

  • 对于链式栈而言,置空栈是回收每一个栈结点。在循环中,由于栈底元素的 next 域始终为 NULL ,故结点的指针域是否为 NULL 可以作为循环的终止条件。
  • 由于栈只能通过栈顶来操作栈结点,所以释放栈顶结点前,要先将栈顶指针下移。
Status ClearStack(LinkStack *S)
{
   //置空栈 S,即仅剩栈顶指针
    SNode *p = *S;
    while (p != NULL)
    {
   
        *S = p->next; //栈顶指针先下移
        free(p);    //释放原栈顶
        p = *S;
    }
    return OK;
}

(3)销毁栈 DestoryStack()

  • 由于链式栈与不带头结点的单链表类似,所以销毁栈操作与置空栈操作的效果一致。最终栈顶指针均都指向 NULL。
Status DestoryStack(LinkStack *S)
{
   //销毁一个栈 S,销毁即释放所有结点
    ClearStack(S);
    free(*S);
    *S = NULL;
    return OK;
}

(4)判空 StackEmpty()

  • 参照栈的初始化,当栈顶指针指向 NULL 时,栈即为空栈。
bool StackEmpty(LinkStack S)
{
   //判断是否为空
    return (S == NULL ? true 
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值