栈分为顺序栈和链式栈,顺序栈是有顺序表的方法实现栈的功能,而链式栈则是用链表的方法实现栈的功能。
顺序栈
结构体
typedef struct seqstack
{
int *data;
int maxlen;
int top;
}seqstack_t;
其中,指针data可以当作数组名来使用,maxlen表示有效元素个数,也就是栈的长度,top是栈针,作用相当于顺序表中的last,始终代表最后一个有效元素的下标。
创建空栈
seqstack_t *CreateEpSeqStack(int len)
{
seqstack_t *p = (seqstack_t *)malloc(sizeof(seqstack_t));
if (p == NULL)
{
perror("create malloc error");
return NULL;
}
p->maxlen = len;
p->top = -1;
p->data = (int *)malloc(sizeof(int) * len);
if (p->data == NULL)
{
perror("data malloc error");
return NULL;
}
return p;
}
和以往的顺序表和链表相同,创建无非开辟空间和初始化两步,对于顺序栈,空间的开辟也是开辟一个结构体类型大小的空间,但是还要开辟一个int类型的数组空间,开辟的大小取决于函数传入的参数len,也就是想要存放多少个数据。此时栈还没有存储任何数据,所以栈针top赋值为-1。
入栈
int PushStack(seqstack_t *p, int data)
{
if (IsFullSeqStack(p))
{
perror("push error");
return -1;
}
p->top++;
p->data[p->top] = data;
return 0;
}
入栈前,栈针指向入栈位置的前一位,入栈时先要将栈针向后移动一个数据的位置,然后在p->top位置直接入栈数据即可。入栈时首先要判断栈是否满,判断是否满的函数如下:
int IsFullSeqStack(seqstack_t *p)
{
return p->top + 1 == p->maxlen;
}
出栈
int PopSeqStack(seqstack_t *p)
{
if (IsEpSeqStack(p))
{
perror("empty");
return -1;
}
p->top--;
printf("%d ",p->data[p->top + 1]);
return 0;
}
出栈时先将栈针向下移动一位,然后输出栈针+1位置的数据,即p->data[p->top + 1]。出栈时要判断栈是否为空:
int IsEpSeqStack(seqstack_t *p)
{
return p->top == -1;
}
获取栈顶数据
int GetTopSeqStack(seqstack_t *p)
{
if (!IsEpSeqStack(p))
{
printf("%d",p->data[p->top]);
return 0;
}
perror("empty");
return -1;
}
获取栈顶数据时也要判断栈是否为空,栈顶数据即栈针指向的数据。
求栈的长度
int LengthSeqStack(seqstack_t *p)
{
return p->top + 1;
}
top为最后一个有效元素的下标,故栈的长度为p->top + 1。
清空栈
void ClearSeqStack(seqstack_t *p)
{
p->top = -1;
}
和顺序表相同,令栈针为-1即可清空顺序栈。
链式栈
结构体
typedef int datatype;
typedef struct linkstack
{
datatype data;
struct linkstack *next;
} linkstack_t;
链式栈的结构体和链表的结构体相同,结构体成员都是一个数据域和指针域。
创建空栈
void CreateEpLinkStack(linkstack_t **ptop)
{
*ptop = NULL;
}
和链表的创建不同,有头链表先要开辟出一个结构体类型大小的空间来存放头节点,而链式栈相当于无头链表,每个节点的数据域都有效,所以创建栈时只需初始化栈针top,注意栈针是在主函数定义的,需要传入子函数进行修改时要传入栈针的地址(二级指针)。
入栈
int PushLinkStack(linkstack_t **ptop, datatype data)
{
linkstack_t *pnew = (linkstack_t *)malloc(sizeof(linkstack_t));
if (pnew == NULL)
{
perror("push malloc error");
return -1;
}
pnew->data = data;
pnew->next = *ptop;
*ptop = pnew;
return 0;
}
入栈时首先要开辟一个结构体类型大小的空间,然后将要入栈的数据赋给该结构体的数据域。而想要用单向链表实现栈的功能(先入后出,后入先出),那必须每次入栈的节点为头节点,即链表的方向是从栈顶指向栈底。在入栈新节点之前,栈针是指向入栈位置的一位,所以新入栈的节点的指针域要指向当前栈顶节点(pnew->next = *ptop),然后移动栈针指向新节点即可。
出栈
datatype PopLinkStack(linkstack_t **ptop)
{
if (IsEpLinkStack(*ptop))
{
perror("empty");
return -1;
}
datatype temp = (*ptop)->data;
linkstack_t *pdel = *ptop;
*ptop = (*ptop)->next;
free(pdel);
return temp;
}
先定义一个临时变量temp保存需要出栈的数据,再定义一个结构体类型的指针pdel指向要出栈的节点,将栈针向栈底移动一位,最后将出栈节点free并返回数据temp,当然链式栈的出栈也要判断栈是否为空:
int IsEpLinkStack(linkstack_t *top)
{
return top == NULL;
}
栈针为空时栈为空。
获取栈顶数据
datatype GetTopLinkStack(linkstack_t *top)
{
if (!IsEpLinkStack(top))
return top->data;
else
{
perror("empty");
return -1;
}
}
如果栈不为空,就返回栈针指向节点的数据域即是栈顶数据。
求栈的长度
int LengthLinkStack(linkstack_t *top)
{
int len = 0;
while (top != NULL)
{
len++;
top = top->next;
}
return len;
}
相当于遍历无头链表。
清空栈
void ClearLinkStack(linkstack_t **ptop)
{
while (!IsEpLinkStack(*ptop))
PopLinkStack(ptop);
}
只要栈不为空,就一直调用出栈函数,直至栈被清空。