1 基本概念
栈是限定仅在表尾进行插入或删除操作的线性表。表头称为栈底,表尾称为栈顶,不含元素的空栈叫做空栈。栈具有后进先出的特点。
2 抽象数据类型(ADT)
数据对象:{a1,a2,.....,an},每个元素类型为DataType,与线性表类似。
数据关系:除第一个元素外,每一个元素有且只有一个直接前驱元素;除最后一个元素外,每个元素有且只有一个直接后驱。一对一 的对应关系。第一个元素被称为栈底元素,最后一个元素为栈顶元素。
基本操作:
InitStack(*S): 建立一个空栈。
DestroyStack(*S): 若栈存在,则销毁它。
ClearStack(*S): 将栈清空。
StackEmpty(S): 若栈为空,返回true,否则返回false。
GetTop(S,*e): 若栈存在且非空,则返回栈顶元素值给e。
Push(*S,e): 插入e为栈顶元素。
Pop(*S,*e): 删除栈顶元素,并将值返回给e。
StackLength(S): 返回栈S的元素个数。
3 顺序存储:
#define ERROR 0
#define OK 1
#define TRUE 1
#define FALSE 0
#include <stdlib.h>
#include <stdio.h>
//栈的顺序存储结构定义
#define STACK_INIT_SIZE 100 //栈的初始存储空间容量
#define STACKINCREMENT 10 //当栈满时,每次新增的存储空间增量
typedef int ElemType; //元素的数据类型根据实际情况而定,这里假设为int
typedef struct Stack
{
ElemType *base; //栈底指针,始终指向栈底,当指向NULL时,则栈结构不存在
ElemType *top; //栈顶指针,指向栈顶元素的下一个元素,当栈为空时,栈顶指针指向栈底
int stacksize; //当前栈的存储空间容量
}SqStack;
int InitStack(SqStack*S) //初始化创建一个栈
{
if (!(S->base=(ElemType*)malloc(STACK_INIT_SIZE*sizeof(ElemType)))) return ERROR; //申请存储空间
S->top=S->base; //栈为空,栈顶指针指向栈底
S->stacksize=STACK_INIT_SIZE; //当前栈的存储空间容量为初始存储空间
return OK;
}
int DestroyStack(SqStack*S) //销毁栈
{
if (S->base)
{
S->stacksize=0;
free(S->base); //释放栈的基地址
S->base=NULL;
S->top=NULL;
}
return OK;
}
int ClearStack(SqStack*S) //清空栈
{
while(S->base!=S->top)
{
S->top--;
}
return OK;
}
int StackEmpty(SqStack S) //验证栈是否为空
{
if (S.base==S.top) return TRUE;
return FALSE;
}
int GetTop(SqStack S,ElemType*e) //得到栈顶元素
{
if (S.base==S.top) return ERROR;
*e=*(S.top-1);
return OK;
}
int Push(SqStack *S,ElemType e) //将元素e压栈
{
if ((S->top-S->base)>=S->stacksize) //若栈已满
{
S->stacksize+=STACKINCREMENT;
if (!(S->base=(ElemType*)realloc(S->base,S->stacksize*sizeof(ElemType)))) return ERROR; //申请新的栈存储空间
}
*(S->top)=e;
S->top++;
return OK;
}
int POP(SqStack *S,ElemType*e) //将栈顶元素出栈,并将值返回给e
{
if (S->base==S->top) return ERROR;
*e=*(--S->top);
return OK;
}
int StackLength(SqStack S) //返回栈的长度
{
return (S.top-S.base);
}
int main() //验证
{
SqStack S;
int e,choice;
do{
printf("输入操作: 1.返回栈长度 2.进栈 3.出栈 4.得到栈顶元素 5.验证栈是否为空 6.清空栈 7.创建栈 8.销毁栈\n");
scanf("%d",&choice);
switch (choice)
{
case 1:
{
e=StackLength(S);
printf("%d\n",e);
break;
}
case 2:
{
printf("输入进栈元素值\n");
scanf("%d",&e);
Push(&S,e);
break;
}
case 3:
{
POP(&S,&e);
printf("%d\n",e);
break;
}
case 4:
{
GetTop(S,&e);
printf("%d\n",e);
break;
}
case 5:
{
printf("%d\n",StackEmpty(S));
break;
}
case 6:
{
ClearStack(&S);
break;
}
case 7:
{
InitStack(&S);
break;
}
case 8:
{
DestroyStack(&S);
break;
}
}
}while(choice!=8);
return 0;
}
4 链式存储
#define ERROR 0
#define OK 1
#define TRUE 1
#define FALSE 0
#include <stdlib.h>
#include <stdio.h>
//栈的链式存储结构定义
typedef int ElemType; //元素的数据类型根据实际情况而定,这里假设为int
typedef struct Node
{
ElemType data;
struct Node*next;
}Node,*LinkStack;
int InitStack(LinkStack*S)
{
(*S)=(Node*)malloc(sizeof(Node)); //头结点
if (!(*S)) return ERROR;
(*S)->next=NULL;
(*S)->data=0; //头结点数据域用来存储栈长度
}
int Push(LinkStack *S,ElemType e) //将元素e压栈
{
Node*p;
p=(Node*)malloc(sizeof(Node));
if (!p) return ERROR;
p->data=e;
p->next=(*S)->next;
(*S)->next=p;
(*S)->data++;
return OK;
}
int POP(LinkStack *S,ElemType*e) //将栈顶元素出栈,并将值返回给e
{
Node*p;
p=(*S)->next;
*e=p->data;
(*S)->next=p->next;
free(p);
(*S)->data--;
return OK;
}
int StackLength(LinkStack S)
{
return S->data;
}
int main()
{
LinkStack S;
Node*p;
int e,choice;
do{
e=0;
printf("输入操作: 0.退出 1.返回栈长度 2.进栈 3.出栈 4.初始化栈\n");
scanf("%d",&choice);
switch (choice)
{
case 1:
{
e=StackLength(S);
printf("%d\n",e);
break;
}
case 2:
{
printf("输入进栈元素值\n");
scanf("%d",&e);
Push(&S,e);
break;
}
case 3:
{
POP(&S,&e);
printf("%d\n",e);
break;
}
case 4:
{
InitStack(&S);
break;
}
}
p=S->next;
printf("[");
while(p)
{
printf("%d ",p->data);
p=p->next;
}
printf("]\n");
}while(choice!=0);
return 0;
}
4 总结
对于顺序栈和链栈,它们时间复杂度都一样,均为O(1)。顺序栈存取更简单方便,但链栈对于顺序栈来说,其长度可无限制。因此在处理元素数量变化不大的情况下,使用顺序栈更方便,否则最好使用链栈。