相比栈的顺序存储的数组结构来说 , 我们采用 链式存储结构,存取更加灵活
栈的存储结构 无非就是数组 和链表 都加上了一个栈顶指针 , 然后来构造栈的结构
接下来 ,我们采用单链表来充当栈的存储结构 , 头结点比较方便定位,所以就让头结点的后继节点当栈顶了
定义存储结构:
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为空的条件是 链表头结点的后继指针指向为空 ,即单链表中没有数据节点
返回相应的结果即可
bool StackEmpty(LiStack *s) { return(s->next == NULL); }
进栈Push(&s,e)
●将数据节点插入到头结点之后
进栈 ,就是把新节点插入到栈顶 ,也就是用头插法,插入到 头结点的后边
传入要进的单链表栈 ,进栈的新元素
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 ,然后将其删除
先判断链表是否为空 ,然后再将头结点的后继节点 ,也就是栈顶指针删除 ,并将其数据通过指针地址传出
//传入要出栈的链栈 , 传入要出栈的指针元素 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
取栈顶元素 和 出栈 的区别是:
取栈顶元素 , 就是如果栈顶指针不为空 , 就把所指的节点数据传出 , 不涉及节点的增删
出栈不仅传出节点 ,而且要把节点删除 , 释放空间 ,多了一步结点连接
//传入要取元素的链栈 ,承载传出的栈顶元素的指针 bool GetTop(LiStack *s , ElemType &e) { //先判断栈是否为空,标志就是栈顶是否为空,栈顶就是头结点的后继节点 if(s->next == NULL) { //为空,则进入返回错误 return false; } //不为空,则继续进行,传出栈顶元素,返回正确 e = s->next->data; return true; }