从0开始学习数据结构-堆栈的链式存储结构

269 阅读4分钟

这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战

堆栈的顺序存储结构的固有缺陷: 在重新调用存储空间时(如多个堆栈共享连续空间),元素的移动量较大,尤其在分配的存储空间即将充满时,这种情况更为突出。 为解决这种问题,可以采用另一种存储方式-堆栈的链式存储

链式存储方式的堆栈通常称为链接堆栈,简称为链栈。

链接堆栈的构造

链接堆栈就是采用一个线性链表实现一个堆栈结构。

  • 栈中每一个元素用一个链结点表示
  • 设置一个指针变量top,指出当前栈顶元素所在链结点的存储位置。
  • 当堆栈为空时,top为NULL
  • 采用线性链表表示的堆栈,链表不必设置头结点,链表的第1个链结点就是栈顶元素所在的链结点
  • 最新进入堆栈的元素一定在链表末尾的那个链结点中 链接堆栈: 根据堆栈的定义
  • 在链接堆栈中插入一个新的元素,实际上就是在该链表的第1个结点之前插入1个新的链结点
  • 删除链接堆栈的栈顶元素,实际上就是删除链表的第1个链结点

只要把线性链表第1个链结点的指针定义为栈顶指针,并且限定只能在链表前面进行插入、删除等操作,这个链表就成了链接堆栈。 链式存储结构:

  • 不需要事先声明一片存储区作为堆栈的存储空间
    • 不存在因为栈满而产生溢出的问题,只要从存储库中可以动态申请到可用链结点空间,就可认为栈没有满。

链接堆栈类型的定义:

typedef struct node {
    SElemType data;
    struct node *link;
} STNode, *STLink; // 定义一个线性链表堆栈类型

在实际问题中,若是不知道或者是难以估计将要进栈元素的最大数量时,采用链式存储结构比采用顺序存储结构更为适合。

时间复杂度: O(1), 因为对链接堆栈的操作都是在链表的表头位置进行的

链接堆栈的基本算法

1.链接堆栈初始化

void INITIALSLINK(STLink &top) // ※
{
    top = NULL;
}

2.判断链接堆栈是否为空

int EMPTYSLINK(STLink top)  // ※
{
    return top == NULL;
}

3.获取栈顶元素

int GETTOPSLINK(STLink top, SElemType &item) // ※
{
    // top 指向栈顶元素所在的链结点
    if (EMPTYSLINK(top))
        return 0; // 堆栈为空,操作失败,返回0
    else {
        item = top->data; // 保存栈顶元素
        return 1; // 堆栈非空,操作成功,返回1
    }
}

4.链接堆栈的插入

链接堆栈的插入相当于在线性链表最前面插入一个新结点。

#define LEN sizeof(STNode)
int PUSHLINK(STLink &top, SElemType item) // ※
{
    // top 指向栈顶元素所在的链结点
    STLink p;
    if (!(p=(STLink)malloc(LEN)))  // ※ 申请一个新的链结点
        return 0; // 插入失败,返回0
    else {
        p->data = item; // 将新结点的数据域设置为item
        p->link = top;  // ※ 将新结点的指针域设置为top
        top = p;  // ※ 修改栈顶指针top的指向
        return 1; // 插入成功,返回1
    }
    
}

image.png

5.链接堆栈的删除

从链接堆栈中删除一个元素相当于删除线性链表的第一个结点。

int POPLINK(STLink &top, SElemType &item) // ※
{
    // top指向栈顶元素所在的链结点
    STLink p;
    if (EMPTYSLINK(top)) // ※
        return 0; // 堆栈为空,删除失败,返回0
    else {
        p = top; // ※
        item = p->data; // 保存被删除结点的数据信息
        top = top->link; // ※
        free(p); // 释放被删除结点
        return 1; // 堆栈非空,删除成功,返回1
    }
}

image.png

堆栈的应用

在实际应用中,只要满足“先进后出(或者后进先出)”的原则,就可以使用堆栈。

  • 符号匹配检查
    • 创建一个空的堆栈,依次读入字符直到文件的末尾
    • 如果读得的字符为左花括号或者左圆括号,则压入堆栈,如果读得的字符是右花括号或者右圆括号,而此时堆栈为空,则出现不匹配线性,提示报错信息;否则,退出当前栈顶元素。
    • 如果退出的栈顶符号不是对用的左花括号或者左圆括号,则出现不匹配情况,提示报错信息
    • 读到文件末尾,若堆栈为空,提示错误信息
  • 数制转换:如十进制转八进制
  • 堆栈在递归中的应用
  • 表达式的计算:中缀表达式,前缀表达式,后缀表达式