栈(stack)是一种只能在一端插入或删除操作的线性表。(既操作受限的线性表)
栈只能在表尾插入或删除元素,表尾就是栈的栈顶,表头就是栈底
栈的主要特点:LIFO(last in first out) "后进先出"
栈可以采用顺序存储结构(顺序栈)和链式存储结构(链式栈)
既然栈可以用顺序存储结构来实现,那为什么还需要链式栈呢?
链栈的优点是不存在栈满上溢的情况
规定,链式栈的所以操作都是在单链表的表头进行的(因为给定链式栈以后,我们可以知道头结点的地址,在其后面插入新结点或删除首结点都很方便,且该操作的时间复杂度为O(1))
下面是采用链式存储结构来实现的链式栈
链式栈的结点类型LinkStack声明:
typedef int ElemType;
typedef struct Node
{
ElemType data;//存储栈的元素值(1个)
struct Node *next;//存储下一个元素(朝栈底方向)节点的地址
}LinkStack;
在以s为头结点指针的链式栈中,有以下四个非常重要的要素
1.栈空条件:s->next==NULL
2.栈满条件:在不考虑内存溢出的情况下,一般不考虑栈满情况
3.进栈操作: 新建一个结点(用于存放元素e),让指针p指向它,将结点p插入到头结点之后
4.出栈操作: 取出首结点存放的值将其删除,并释放内存
1) 链栈的初始化
创建一个空的链栈s(实际上是创建头结点),代码如下:
LinkStack *InitStack()
{
LinkStack *s;
s=(LinkStack *)malloc(sizeof(LinkStack));
s->next=NULL;
return s;
}
2)销毁栈
销毁链式栈的方法与销毁单链表的操作完全相同,代码如下:
void DestroyStack(LinkStack *s)
{
ElemType m;
int Pop(LinkStack *s,ElemType *e);
while(s->next!=NULL)//不空
{
//出栈
Pop(s,&m);
}
free(s);
}
3)判断栈是否为空
判断栈是否为空是只需要判断s->next == NULL成立即可,代码如下:
int StackEmpty(LinkStack *s)
{
if(s->next==NULL)
{
return 1;
}
else
{
return 0;
}
}
如果你觉得上述代码太过于繁琐,也可以这样写:
int StackEmpty(LinkStack *s)
{
return (s->next == NULL);
}
4)进栈(push)
进栈,也称入栈,执行该操作需要无判断栈是否已满,因为链式栈不存在栈满操作,代码如下:
void *Push(LinkStack *s,ElemType e)
{
LinkStack *t;
//1.构造一个节点t,存储元素值e
t=(LinkStack *)malloc(sizeof(LinkStack));
t->data=e;
//2.把t节点添加到头节点的后面
t->next=s->next;
s->next=t;
}
5)出栈(Pop)
出栈,也称退栈,执行该操作需要先判断栈是否为空,若栈不空,则将链式栈的首结点的数据域存储到*e,再将其删除,代码如下:
/*
若栈不空,进行相关操作,并返回1;
否则,提示,返回0
*/
int Pop(LinkStack *s,ElemType *e)
{
LinkStack *t;
if(s->next!=NULL)//不空
{
//1.让t指向栈顶元素节点
t=s->next;
//2.把栈顶元素值存储到*e中
*e=t->data;
//3.删除
s->next=t->next;
//4.释放存储空间
free(t);
return 1;
}
else
{
printf("栈空,不能出栈!\n");
return 0;
}
}
6)取栈顶元素
取栈顶元素也需要,判断栈是否为空,以确保操作合法性,代码如下:
/*
若栈不空,进行相关操作,并返回1;
否则,提示,返回0
*/
int GetTop(LinkStack *s,ElemType *e)
{
if(s->next!=NULL)//不空
{
*e=s->next->data;
return 1;
}
else
{
printf("栈空,不能取栈顶元素!\n");
return 0;
}
}
7)输出栈
void display(LinkStack *s)
{
while(s->next != NULL)
{
printf("%d",s->next->data);
s = s->next;
}
}
下面是入栈、退栈和取栈顶元素以及输出栈的元素的操作,代码如下:
int main()
{
LinkStack s;
s = *InitStack();
Push(&s,1);
Push(&s,2);
Push(&s,3);
Push(&s,4);
Push(&s,5);
Push(&s,6);
Push(&s,7);
ElemType *e;
e = (ElemType *)malloc(sizeof(ElemType));
GetTop(&s,e);
printf("%d\n",*e);
Pop(&s,e);
GetTop(&s,e);
printf("%d\n",*e);
display(&s);
printf("\n");
GetTop(&s,e);
printf("%d\n",*e);
return 1;
}
测试结果如下:
最后在附上完整代码:
#include
typedef int ElemType;
typedef struct Node
{
ElemType data;//存储栈的元素值(1个)
struct Node *next;//存储下一个元素(朝栈底方向)节点的地址
}LinkStack;
/*
空:s->next==NULL
*/
/*
1.初始化
*/
LinkStack *InitStack()
{
LinkStack *s;
s=(LinkStack *)malloc(sizeof(LinkStack));
s->next=NULL;
return s;
}
/*
2.销毁
*/
void DestroyStack(LinkStack *s)
{
ElemType m;
int Pop(LinkStack *s,ElemType *e);
while(s->next!=NULL)//不空
{
//出栈
Pop(s,&m);
}
free(s);
}
/*
3.判断栈是否为空
若为空,返回1;
否则,返回0
*/
int StackEmpty(LinkStack *s)
{
if(s->next==NULL)
{
return 1;
}
else
{
return 0;
}
}
/*
4.进栈
*/
void *Push(LinkStack *s,ElemType e)
{
LinkStack *t;
//1.构造一个节点t,存储元素值e
t=(LinkStack *)malloc(sizeof(LinkStack));
t->data=e;
//2.把t节点添加到头节点的后面
t->next=s->next;
s->next=t;
}
/*
5.出栈
若栈不空,进行相关操作,并返回1;
否则,提示,返回0
*/
int Pop(LinkStack *s,ElemType *e)
{
LinkStack *t;
if(s->next!=NULL)//不空
{
//1.让t指向栈顶元素节点
t=s->next;
//2.把栈顶元素值存储到*e中
*e=t->data;
//3.删除
s->next=t->next;
//4.释放存储空间
free(t);
return 1;
}
else
{
printf("栈空,不能出栈!\n");
return 0;
}
}
/*
6.取栈顶元素
若栈不空,进行相关操作,并返回1;
否则,提示,返回0
*/
int GetTop(LinkStack *s,ElemType *e)
{
if(s->next!=NULL)//不空
{
*e=s->next->data;
return 1;
}
else
{
printf("栈空,不能取栈顶元素!\n");
return 0;
}
}
/*
7.输出
从栈顶 至 栈底
*/
void display(LinkStack *s)
{
while(s->next != NULL)
{
printf("%d",s->next->data);
s = s->next;
}
}
int main()
{
LinkStack s;
s = *InitStack();
Push(&s,1);
Push(&s,2);
Push(&s,3);
Push(&s,4);
Push(&s,5);
Push(&s,6);
Push(&s,7);
ElemType *e;
e = (ElemType *)malloc(sizeof(ElemType));
GetTop(&s,e);
printf("%d\n",*e);
Pop(&s,e);
GetTop(&s,e);
printf("%d\n",*e);
display(&s);
printf("\n");
GetTop(&s,e);
printf("%d\n",*e);
return 1;
}