数据结构之栈
程序等于数据结构加算法。
所以,在计算机里面,算法和数据结构相当于最重要的两门课了。以前上课没怎么上心,正好借着写博客的机会好好把数据结构这部分内容整理一下。
栈(stack)是限定仅在表尾进行插入或者删除操作的线性表。
不含元素的空表称为空栈。
对栈来说,表尾端为栈顶(top),也就是我画的图中的an部分为栈顶元素。表头端称为栈底(bottom),也就是a1部分为栈底元素。
栈有另外一种说法是 后进先出 的线性表(简称LIFO结构)。
也就是栈中元素按照a1,a2,a3,…,an的顺序进栈。当出栈的时候再按照an,…,a2,a1的顺序出栈。
所以我们就能看出栈这种数据结构具有后进先出的明显特点。
栈的表示与实现。
与线性表类似,栈也有两种存储表示方法。分别是顺序栈与链栈。
顺序栈
顺序栈,即栈的顺序储存结构是利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,也就是本例中存放a1到an的元素,同时附设指针top指示栈顶元素(本例中an)在顺序栈中的位置。
当top=0时,就是an的位置为0,意味着这是一个空栈。
因为像在c语言,Java中的数组下标都是从0开始,所以所以用c或Java作语言时,就不方便。
还因为栈在使用过程中所需要的最大空间的的大小不便估计,所以,一般初始化设空栈时不限定栈的最大容量。
一个合理的做法是,先为栈分配一个基本容量,当我们在应用的时候,如果栈的空间不够使用时再逐步扩大。
我们可以先设定两个常量:
STACK_INT_SIZE(纯纯空间初始分配量)
STACKINCREMENT(储存空间分配增量)
以下类型作为程序栈的定义;
typedef struct{
SElemType *base;
SElemType *top;
int stacksize;
}SqStack;
其中stacksize为栈当前可使用的最大的容量,*base为指向栈底部的指针,*top为指向栈顶部的指针。
*base:
在顺序栈中,始终指向栈底的位置,如果base指向值为NULL,则表示栈结构不存在
*top:
在顺序栈中,*top是变化的,开始时会指向栈底。**如果top=base时,即是栈空的标记。**当插入新的栈顶元素时,指针top就会加1。删除栈顶元素时,指针top就会减1。
在栈中,非空栈中的栈顶指针始终指向栈顶的元素的下一个位置上。
下图表示数据元素和栈顶指针之间的对应关系。
下面是一些栈的基本操作的算法部分
构造一个空栈
Status InitStack(SqStack &S){
//构造一个空栈
S.base = (SElemType * )malloc(STACK_INIT_SIZE * sizeof(SElemType));
if(!S.base) exit (OVERFLOW);//存储分配失败的情况
S.top=S.base;//栈顶等于栈底
S.stacksize = STACK_INIT_SIZE;//STACK_INIT_SIZE为储存空间初始分配量
return OK;
}
得到栈顶元素
Status GetTop(SqStack S,SElemType &e){
//如果栈不空,则用e返回S的栈顶元素,并且返回OK,否则返回ERROR
if(S.top==S.base) return ERROR;//如果空栈返回error
e =*(S.top-1);//栈顶元素如上图,在top指针下面一个
return OK;
}
插入
Status Push(SqStack &S,SElemType e){
//在栈中插入值为e的新栈顶元素
if(S.top-S.base>=S.stacksize){
S.base = (SElemType *)realloc (S.base,(S.stacksize + STACKINCREMENT) * sizeof(SElemType));
if(!S.base) exit (OVERFLOW);//储存分配失败
S.top = S.base + S.stacksize;
S.stacksize += STACKINCREMENT;//新储存空间等于旧的加要加的
}
*S.top++ =e;//指针上移
return OK;
}
删除栈顶元素
Status Pop(SqStack &S,SElemType e){
//若栈不空,则删除S的栈顶元素,用e返回值,
if(S.top==S.base) return ERROR;
e = * --S.top;
return OK;
}
链栈
链栈也是栈。
链栈是指采用链接存储的结构实现的栈。看上去与列表相像。下面是我手画的图。
其中data为数据,next为下一个数据的指针。
就这样把数据链接起来。
需要注意的是链栈不存在栈满的情况,不用像正常的顺序栈一样去判断是否栈满。
链栈置空算法
LinkStack * SetStack(){
LinkStack *LS;
LS=NULL;
return LS;
}
链栈取顶
datatype GetTop(LinkStack *LS,ELemType &e){
if(LS!=NULL) e=LS->data;
return OK;
else
return ERROR;
}
、
栈被经常用到是因为具有后进先出的特点。所以当我们在编程时,如果目标需要这个特点,那么就往栈方面去考虑吧。下次再写一篇博客探讨栈的实际应用。