一、知识准备
1)c语言结构体指针元素不能赋值,C语言结构体指针必须被结构体变量赋地址或malloc后才能正常使用,不能单独赋值?..._蓝星神的博客-CSDN博客
2)二级指针的作用详解_码农在新加坡的博客-CSDN博客_二级指针
二、链栈的操作函数
0)链栈的定义和一些宏定义
#define ElementType char//如果要修改数据域类型,修改这里的宏定义,入栈出栈函数的printf,显示栈的printf,主函数的scanf和printf
#define LEN sizeof(LinkStackNode)
int flag=0;//由于链栈的初始化会出现一个结点并且数据域没有赋值,所以定义一个全局变量辅助第一次入栈,即只赋值但是不开辟新节点,在销毁链栈时再让flag=0
//链栈的结构是首地址没有数据域赋值,上面结点指向下面结点
typedef struct LinkStackNode{
ElementType Data;
struct LinkStackNode* Next;
}LinkStackNode,*LinkStackPointer;//第一个参数指链栈结点只是单纯的给宏定义LEN用的,第二个是指指向链栈的指针变量
1)初始化链栈---明确这里的链栈结构首结点的数据域是有赋值的,这样方便操作
//初始化链栈,传入的是一级指针的地址,因为要修改指针变量的值,所以要用地址传递,也就是二级指针修改一级指针的值
void Init_LinkStack(LinkStackPointer* Top){
(*Top)=(LinkStackPointer)malloc(LEN);
if((*Top)==NULL){
printf("初始化链栈失败!\n\n");
exit(0);
}
else{
printf("初始化链栈成功!\n\n");
(*Top)->Next=NULL;
}
}
2)判断链栈是否为空
//判断是否为空链栈,只需传入指向链栈首地址的指针变量即可
int LinkStack_Empty(LinkStackPointer Top){
if(Top==NULL){
return 0;
}
else return 1;
}
3)计算链栈元素个数
//计算链栈元素个数,只需传入一级指针即可
int LinkStack_Length(LinkStackPointer Top){
LinkStackPointer Temp=Top;
int length=0;
while(Top!=NULL){
length++;
Top=Top->Next;
}
Top=Temp;
return length;
}
4)入栈---基本不出现溢出现象
//入栈,使用二级指针,因为要修改指向链栈头节点地址的指针变量的值
void Push_LinkStack(LinkStackPointer* Top,ElementType data){
//因为初始化的时候开辟的结点没有赋值,为了后续操作方便在这里先赋值,下一次再开辟
if(flag==0){//为了确保销毁链栈后还要入栈的操作
(*Top)->Data=data;
flag=1;
printf("%c元素入栈成功!\n",(*Top)->Data);
}
else{
LinkStackPointer Temp=(LinkStackPointer)malloc(LEN);
if(Temp==NULL){
printf("申请内存失败!\n");
}
else{
Temp->Data=data;
Temp->Next=(*Top);
(*Top)=Temp;
printf("%c元素入栈成功!\n",(*Top)->Data);
}
}
}
5)出栈---时时刻刻判断是否为空
//出栈,使用二级指针,因为要修改指向链栈头节点地址的指针变量的值
void Pop_LinkStack(LinkStackPointer* Top,ElementType* data){
LinkStackPointer Temp;
if((*Top)==NULL){
printf("出栈失败,链栈已空!\n");
}
else{
Temp=(*Top)->Next;
*data=(*Top)->Data;
free(*Top);
(*Top)=Temp;
printf("%c元素已弹出!\n",*data);
}
}
6)销毁链栈
//销毁链栈,使用二级指针,因为每次释放一个结点指向链栈头结点地址的指针变量的值要改变,最终指针变量的值为0
void Destroy_LinkStack(LinkStackPointer* Top){
LinkStackPointer Temp;
while((*Top)!=NULL){
Temp=(*Top)->Next;
free(*Top);
(*Top)=Temp;
}
printf("\n销毁链栈成功!\n");
flag=0;
}
7)显示链栈
//显示链栈,一级指针
void Show_LinkStack(LinkStackPointer Top){
LinkStackPointer Temp=Top;
int i=1;
if(Top==NULL){
printf("链栈为空,无法显示!\n");
}
else{
do{
printf("第%d个结点的元素是%c\n",i++,Temp->Data);
Temp=Temp->Next;
}while(Temp!=NULL);
}
}
三、实践
#include
#include
#define ElementType char//如果要修改数据域类型,修改这里的宏定义,入栈出栈函数的printf,显示栈的printf,主函数的scanf和printf
#define LEN sizeof(LinkStackNode)
int flag=0;//由于链栈的初始化会出现一个结点并且数据域没有赋值,所以定义一个全局变量辅助第一次入栈,即只赋值但是不开辟新节点,在销毁链栈时再让flag=0
//链栈的结构是首地址没有数据域赋值,上面结点指向下面结点
typedef struct LinkStackNode{
ElementType Data;
struct LinkStackNode* Next;
}LinkStackNode,*LinkStackPointer;//第一个参数指链栈结点只是单纯的给宏定义LEN用的,第二个是指指向链栈的指针变量
//初始化链栈,传入的是一级指针的地址,因为要修改指针变量的值,所以要用地址传递,也就是二级指针修改一级指针的值
void Init_LinkStack(LinkStackPointer* Top){
(*Top)=(LinkStackPointer)malloc(LEN);
if((*Top)==NULL){
printf("初始化链栈失败!\n\n");
exit(0);
}
else{
printf("初始化链栈成功!\n\n");
(*Top)->Next=NULL;
}
}
//判断是否为空链栈,只需传入指向链栈首地址的指针变量即可
int LinkStack_Empty(LinkStackPointer Top){
if(Top==NULL){
return 0;
}
else return 1;
}
//计算链栈元素个数,只需传入一级指针即可
int LinkStack_Length(LinkStackPointer Top){
LinkStackPointer Temp=Top;
int length=0;
while(Top!=NULL){
length++;
Top=Top->Next;
}
Top=Temp;
return length;
}
//入栈,使用二级指针,因为要修改指向链栈头节点地址的指针变量的值
void Push_LinkStack(LinkStackPointer* Top,ElementType data){
//因为初始化的时候开辟的结点没有赋值,为了后续操作方便在这里先赋值,下一次再开辟
if(flag==0){//为了确保销毁链栈后还要入栈的操作
(*Top)->Data=data;
flag=1;
printf("%c元素入栈成功!\n",(*Top)->Data);
}
else{
LinkStackPointer Temp=(LinkStackPointer)malloc(LEN);
if(Temp==NULL){
printf("申请内存失败!\n");
}
else{
Temp->Data=data;
Temp->Next=(*Top);
(*Top)=Temp;
printf("%c元素入栈成功!\n",(*Top)->Data);
}
}
}
//出栈,使用二级指针,因为要修改指向链栈头节点地址的指针变量的值
void Pop_LinkStack(LinkStackPointer* Top,ElementType* data){
LinkStackPointer Temp;
if((*Top)==NULL){
printf("出栈失败,链栈已空!\n");
}
else{
Temp=(*Top)->Next;
*data=(*Top)->Data;
free(*Top);
(*Top)=Temp;
printf("%c元素已弹出!\n",*data);
}
}
//销毁链栈,使用二级指针,因为每次释放一个结点指向链栈头结点地址的指针变量的值要改变,最终指针变量的值为0,所以每次销毁栈后还要入栈必须先初始化
void Destroy_LinkStack(LinkStackPointer* Top){
LinkStackPointer Temp;
while((*Top)!=NULL){
Temp=(*Top)->Next;
free(*Top);
(*Top)=Temp;
}
printf("\n销毁链栈成功!\n");
flag=0;
}
//显示链栈,一级指针
void Show_LinkStack(LinkStackPointer Top){
LinkStackPointer Temp=Top;
int i=1;
if(Top==NULL){
printf("链栈为空,无法显示!\n");
}
else{
do{
printf("第%d个结点的元素是%c\n",i++,Temp->Data);
Temp=Temp->Next;
}while(Temp!=NULL);
}
}
int main(){
//变量定义
LinkStackPointer Top;
ElementType c;
printf("注意:销毁链栈后再次入栈必须进行初始化,否则会出错!\n\n");
//初始化链栈,传入二级指针
Init_LinkStack(&Top);
//链栈的简单入栈
Push_LinkStack(&Top,'c');
Push_LinkStack(&Top,'b');
Push_LinkStack(&Top,'a');
//判断此时是否入栈正确
printf("\n链栈有%d个元素\n",LinkStack_Length(Top));
//显示一下链栈的内容
printf("\n");
Show_LinkStack(Top);
printf("\n");
//链栈2次出栈
Pop_LinkStack(&Top,&c);
Pop_LinkStack(&Top,&c);
//判断此时是否出栈正常
printf("\n链栈有%d个元素\n",LinkStack_Length(Top));
//销毁链栈
Destroy_LinkStack(&Top);
//判断此时销毁是否正常
printf("\n链栈有%d个元素\n",LinkStack_Length(Top));
//判断显示链栈这个函数是否正常
Show_LinkStack(Top);
//销毁后入栈必须初始化
printf("\n");
Init_LinkStack(&Top);
printf("\n");
//销毁后再次入栈实验
Push_LinkStack(&Top,'x');
Push_LinkStack(&Top,'w');
Push_LinkStack(&Top,'e');
//判断再次入栈是否正确
printf("\n链栈有%d个元素\n",LinkStack_Length(Top));
//销毁后显示栈内容
printf("\n");
Show_LinkStack(Top);
printf("\n");
return 0;
}