目录
1.链栈
链栈和单链表是差不多的,只是规定了入栈和出栈只能在表头一端进行(栈顶)。
注:链栈相比于顺序栈(动态)来说会比较方便的是不用担心插入节点是否出现空间满的问题,但是链栈中的节点之间不是连续的存储关系,逻辑上是连续的,但是在内存中的存储是分布散开的。
注:以上是带头结点的链栈。其实在实际中我们更多的时候是会关注的是栈的实际应用,而不是仅仅关注的是栈本身的简单实现,所以学会了栈的基本操作之后,学会将栈应用到实际中非常的重要,也是对自己的挑战。
2.链栈的相关操作
(1)数据结构定义
#include<stdlib.h>
#include<stdio.h>
#define MAXSIZE 10
typedef int ElemType;
typedef struct Linknode {
ElemType data;
struct Linknode*next;
}*LiStack;
//菜单栏
void MenuSqStack(){
printf("----------1.入栈元素--------\n");
printf("----------2.获取栈顶元素----\n");
printf("----------3.弹出栈顶元素----\n");
printf("----------4.重新初始化------\n");
printf("----------5.退出操作--------\n");
}
(2)初始化和销毁
首先是带头结点:
//初始化
void InitLiStack(LiStack&s){
s=(Linknode*)malloc(sizeof(Linknode));
if(s==NULL){
printf("内存分配失败!\n");
return ;
}
s->next=NULL;
printf("空间申请成功!\n");
}
//销毁
void DestryLiStack(LiStack&s){
Linknode*p=s->next;
while(p!=NULL){
Linknode*r=p;
p->next=r->next;
free(r);
}
free(s);
printf("销毁成功!\n");
}
不带头结点:
//初始化
void InitLiStack(LiStack&s){
s=NULL;
printf("申请空间成功!\n");
}
//销毁
void DestryLiStack(LiStack&s){
Linknode*p=s;
while(p->next!=NULL){
Linknode*r=p;
p=r->next;
free(r);
}
//最后一个节点特殊处理
free(p);
printf("销毁成功!\n");
}
(3)入栈
带头结点:
//入栈
void PushLiStack(LiStack&s,ElemType e){
Linknode*r=(Linknode*)malloc(sizeof(Linknode));
r->data=e;
r->next=s->next;
s->next=r;
printf("插入节点成功!\n");
}
不带头结点:
相比于带头点的链栈来说相对较复杂,因为开始的时候s==NULL,所以首选需要将插入的第一个节点做特殊的处理。
//入栈
void PushLiStack(LiStack&s,ElemType e){
//如果开始没有节点,特殊处理
if(s==NULL){
Linknode*r=(Linknode*)malloc(sizeof(Linknode));
r->data=e;
r->next=NULL;
s=r;
printf("插入节点成功!\n");
return ;
}
Linknode*r=(Linknode*)malloc(sizeof(Linknode));
//首先将头结点的值修改为要插入的值e,将r->data=s->data,最后直接在头结点后面插入节点 r即可
r->data=s->data;
s->data=e;
r->next=s->next;
s->next=r;
printf("插入节点成功!\n");
}
(4)出栈
带头结点:
//出栈
void PopLiStack(LiStack&s,ElemType&e){
if(s->next==NULL){
printf("栈空!\n");
return;
}
Linknode*p=s->next;
e=p->data;
s->next=p->next;
free(p);
printf("出栈成功!\n");
}
//获取栈顶节点
Linknode*GetElem(LiStack&s,ElemType&e){
if(s->next==NULL){
printf("栈空!\n");
return NULL;
}
Linknode*p=s->next;
e=p->data;
}
不带头结点:
删除的操作其实不一定像我这样写,我只是做了一个最后一个节点的特殊处理。
//出栈
void PopLiStack(LiStack&s,ElemType&e){
if(s==NULL){
printf("栈空!\n");
return;
}
//只有一个节点的时候特殊处理
if(s->next==NULL){
e=s->data;
free(s);
return ;
}
Linknode*p=s;
e=p->data;
s->next=p->next;
free(p);
printf("出栈成功!\n");
}
//获取栈顶节点
Linknode*GetElem(LiStack&s,ElemType&e){
if(s==NULL){
printf("栈空!\n");
return NULL;
}
Linknode*p=s;
e=p->data;
}
(5)主函数
主函数对于带头结点和不带头结点都是一样的。
int main() {
LiStack s;
ElemType e;
InitLiStack(s);
while(1){
int flag=0;
bool popFlag=false;
Linknode*p;
MenuSqStack();
printf("请输入操作: ");
scanf("%d",&flag);
switch(flag){
case 1:
printf("请输入元素(-1_end): ");
scanf("%d",&e);
while(e!=-1){
PushLiStack(s,e);
printf("请输入元素(-1_end): ");
scanf("%d",&e);
}
break;
case 2:
p=GetElem(s,e);
if(p!=NULL){
printf("栈顶元素 = %d\n",e);
}
break;
case 3:
PopLiStack(s,e);
printf("弹出栈顶元素 = %d\n",e);
break;
case 4:
//首先要销毁之后再重新申请
DestryLiStack(s);
InitLiStack(s);
break;
default:
DestryLiStack(s);
printf("结束操作\n");
}
if(flag==5){
break;
}
}
return 0;
}
(6)测试
首先是带头结点的测试;

不带头结点的测试: