18.11.18
学习过之前的顺序的栈了,这回介绍一下链式的栈
我们对栈进行操作时,通常都是在其栈顶进行,所以我们应该在链的头部进行操作,我们之前学过的链表大多是有头结点的,头结点可以大大方便我们对链表的调用,现在,我们的这个链栈可以通过设置一个栈顶指针top来代替头结点,或者说起到头结点的作用
结构定义
链栈有些不同的是,我们需要定义两个结构体,比之前稍增加了点难度,第一个结构体,是用来定义链栈中的每一个结点的(包括top结点)的结构,即StackNode,第二个结构体,是用来定义栈顶指针的,其包含top结点以及一个计数器count,我们通常将第二个结构体命名为LinkStack,因为当我们需要压栈和出栈时,需要调用函数,调用函数时,需要传入参数,一般都是传入栈顶指针来对整个链栈进行操作的,所以直接把栈顶指针命名为LinkStack(即链栈),就应该是它可以代表整个链栈了吧(个人理解)
typedef int SElemType;
//这里将SElemType等同于int,实际运用中数据不一定是int的,可以改变
typedef struct StackNode{//这里设置链栈的结点结构
//每个结点包含一个数据域和一个指针域
SElemType data;
struct StackNode *next;
}StackNode,*pNode;//*pNode是结点的结构体指针
typedef struct LinkStack{//这里设置栈顶指针
//包含了top结点的地址和一个计数器count
pNode top;
int count;
}LinkStack;
链栈初始化
跟链表很像,链表是头结点指向NULL,这里是top结点指向NULL,同时将计数器置0
void InitStack(LinkStack *ST){//初始化函数
ST->top=NULL;//top结点指向空
ST->count=0;//置0
}
压栈(入栈)
现在是只有栈顶指针的空栈,我们要加入一下元素,就要压栈
思路是创造一个新的结点,将要加入的数据存入里面,在将新结点压入就行了,具体代码如下
void Insert(LinkStack *ST,SElemType e){//将栈顶指针地址和将要入栈的元素传入
pNode s=(pNode)malloc(sizeof(StackNode));//给新结点分配空间
s->data=e;
s->next=ST->top;
ST->top=s;
ST->count++;//每压入一个元素,栈的计数器加1
}
出栈
我们不仅要给函数传入一个栈顶指针地址,还要传入一个整型指针的地址来保存弹出的元素,记得事后将弹出的结点free掉
int Pop(LinkStack *ST,SElemType *e){
pNode p;//创造一个结点p,用于存储要弹出结点的地址
if(ST->count==0)//如果是空栈就不能弹出了
return 0;
p=ST->top;//存入要弹出的地址
*e=ST->top->data;//保存要弹出的元素
ST->top=ST->top->next;//栈顶指针向后指
ST->count--;//计数器减1
free(p);//释放掉弹出结点的空间
return 1;
}
打印栈
需要一个定位指针,通过其遍历整个链栈,打印遍历到的每个结点的data域上的值
int Print(LinkStack *ST){//打印函数
pNode H=ST->top;//定位指针
if(H==NULL)//如果定位指针都是指向空的,说明没什么好打印的了
return 0;
printf("此时栈为: ");
printf("%d ",H->data);
while(H->next!=NULL){
H=H->next;
printf("%d ",H->data);
}
printf("\n");
return 1;
}
源程序
将上述的函数合并起来进行调试
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
typedef int SElemType;
typedef struct StackNode{//这里设置链栈的结点结构
//每个结点包含一个数据域和一个指针域
SElemType data;
struct StackNode *next;
}StackNode,*pNode;//*pNode是结点的结构体指针
typedef struct LinkStack{//这里设置栈顶指针
pNode top;
int count;
}LinkStack;
//函数声明
void InitStack(LinkStack *ST);
void Insert(LinkStack *ST,SElemType e);
int Pop(LinkStack *ST,SElemType *e);
int Print(LinkStack *ST);
int main(){//主函数调试
LinkStack ST;
int i,e;
InitStack(&ST);
for(i=0;i<10;i++){
Insert(&ST,i);
}
Print(&ST);
for(i=0;i<5;i++){
Pop(&ST,&e);
printf("弹出了一个数字%d,",e);
Print(&ST);
printf("\n");
}
Print(&ST);
}
void InitStack(LinkStack *ST){//初始化函数
ST->top=NULL;//top结点指向空
ST->count=0;//置0
}
void Insert(LinkStack *ST,SElemType e){//将栈顶指针地址和将要入栈的元素传入
pNode s=(pNode)malloc(sizeof(StackNode));//给新结点分配空间
s->data=e;
s->next=ST->top;
ST->top=s;
ST->count++;//每压入一个元素,栈的计数器加1
}
int Pop(LinkStack *ST,SElemType *e){
pNode p;//创造一个结点p,用于存储要弹出结点的地址
if(ST->count==0)//如果是空栈就不能弹出了
return 0;
p=ST->top;//存入要弹出的地址
*e=ST->top->data;//保存要弹出的元素
ST->top=ST->top->next;//栈顶指针向后指
ST->count--;//计数器减1
free(p);//释放掉弹出结点的空间
return 1;
}
int Print(LinkStack *ST){//打印函数
pNode H=ST->top;//定位指针
if(H==NULL)//如果定位指针都是指向空的,说明没什么好打印的了
return 0;
printf("此时栈为: ");
printf("%d ",H->data);
while(H->next!=NULL){
H=H->next;
printf("%d ",H->data);
}
printf("\n");
return 1;
}
调试结果
成功啦!!
总结
- 链栈跟链表很像
- 创建使一定要记清楚自己写的结构体里边到底包含了什么!!!
- 自己给它起的什么名字!!!
- 永远要清楚自己的指针指向了什么地方!!!