这是我参与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
}
}
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
}
}
堆栈的应用
在实际应用中,只要满足“先进后出(或者后进先出)”的原则,就可以使用堆栈。
- 符号匹配检查
- 创建一个空的堆栈,依次读入字符直到文件的末尾
- 如果读得的字符为左花括号或者左圆括号,则压入堆栈,如果读得的字符是右花括号或者右圆括号,而此时堆栈为空,则出现不匹配线性,提示报错信息;否则,退出当前栈顶元素。
- 如果退出的栈顶符号不是对用的左花括号或者左圆括号,则出现不匹配情况,提示报错信息
- 读到文件末尾,若堆栈为空,提示错误信息
- 数制转换:如十进制转八进制
- 堆栈在递归中的应用
- 表达式的计算:中缀表达式,前缀表达式,后缀表达式