各位看官好,今天我们来讲讲栈。因为我觉得大家可能对这个了解较少所以鄙人就直接开始了。
栈的理论
- 栈是一个先进后出的结构,类似于堆盘子,先放到地上的盘子最后被取走(默认只能取走一个盘子)
- 栈其实就是操作受限的线性表,只有一个口,每一次操作时,这个口可以当出口也可以当入口.
- 例如:暖水瓶,注入水时,水瓶的头当做入口,倒水时,水瓶的头当做出口
图解
上面的图片也可以再次证明栈的操作特性是先进后出。
代码实现
栈定义
那么接下来我们就尝试用代码来实现栈,我们知道栈这个东西首先要有吧。所以我们就先来创建栈。既然我们需要创建一个栈,就用结构体。结构体里面有什么东西嘞。我们就简单点首先要有内容吧。然后就是栈顶、栈底。因为我们上面写了入栈和出栈。
typedef struct stack{
int StackSize;
int top;
int base;
}stack;
栈初始化
那么我们定义化了栈后,不能只创建啊,那么我们要把栈初始化嘛。使用 == malloc()==函数,base是栈底指针,始终指向栈底(s.top=s.base为栈空)。top为栈顶指针,top初值指向栈底,每当插入一个元素时top+1,弹出一个元素时top-1,所以,非空栈中的栈底指针始终在栈顶元素的下一个位置。
Status InitStack(SqStrack &S)
{
S.base=(SdataType *)malloc(STACK_INI_SIZE*sizeof(SDataType));//给栈分配内存空间
if(!S.base)
{
perror(“malloc s”)
exit(1);
}
S.top=S.base; //起初S.base指向S.top的位置,为空栈,之后有元素入栈,top向后移,base永远不变
S.StrackSize=STACK_INIT_SIZE;// 表示栈的内存大小
return OK;
}
是否空栈
既然我们把栈处理了。我们就来搞一些要思考的。我们判断是否空栈,因为我们在开始创建的时候就创建了栈顶与栈底。那我们看看下面的图片。
是不是如果栈顶与栈底相等的话,那就是空栈了。这下大家应该就差不多了解判断空栈如何实现了吧。
void JudgeNull(SqStack &s)
{
if(s.top==s.base)
return 0;
}
是否满栈
既然我们写了是否空栈后,那么是否满栈也要安排上吧。那么我们思考一下怎么判断是否为满栈嘞。大家应该还记得我们在开始创建的时候还写了一个size吧。size表示当前已经分配的内存空间,可以用s.top-s.base来算当前使用的栈空间,即当s.top-s.base=s.StrackSize时表示满栈。(s.top永远不指向值,s.top指向存储最后一个值的下一个,比如给栈申请了100个空间,s.top指向101,所以栈里存放了100个值)
void JudgeFull(SqStrack &S)
{
if(s.top-s.base==s.StrackSize)
return 1;//栈满返回1
else
return 0;//栈未满返回0
}
遍历栈
大家都知道我们开始写的数组,写完了都要打印一下的啊。那么我们这个栈也不能少吧。那么我们接下来就写下如何将栈中的数据打印出来。大家想一想我们栈的特性是什么就是先进后出,是吧。那么我们想看到我们我们写入的顺序那么我们是不是就要倒着输出啊。那么我们写的栈顶就有用,因为我们栈顶是开头嘛。那么我们就栈顶开始输出。但需要注意的是我们的栈顶一定的栈的插入元素的个数减一。
大家看了上面的图片想一想为什么栈顶要减一啊。这是不是我们上面判空是不是当top与dase相等就是空,那如果我们top的下一个是dase这样我们就满了。我们留一个位置不放数据。当然这是前面应该写的与这里涉及的知识不太相同。然后我们这里只需要倒着循环打印就可以了。
void StackPrint(strack *p)
{
while(p->top!=p->base) //栈不为空
{
printf("%c",p->data[p->top-1]); //输出每一个数据
p->top--; //栈顶-1,直到栈为空
}
}
栈顶入数据
前面都写了判断了,那么我们不能只写判断这些啊。我们要往里面放东西啊。是吧,就好比是数组入数据。但是我们入数据哇,肯定要判断还能不能放进去嘛,是吧。如果我们水缸都满了我们还往水缸里面放水,岂不是溢出来了,为了防止溢出那么我们就要新的缸来乘水吧。所以我们前面的写的判满就用上了。
//入栈
Status Push(SqStack &s,SDataType e){
SDataType *p;
//首先判断栈是不是满的(上溢)
if(s.top-s.base == s.StackSize){
//追加空间
p = (SDataType *)realloc(s.base,(s.StackSize + STACKINCREMENT)*sizeof(SDataType));
if(!p){
//如果没有找到符合条件的存储空间,则返回error
return OVERFLOW;
}
//成功找到则使s.base指向p
s.base = p;
s.top = s.base + s.StackSize;
s.StackSize += STACKINCREMENT;
}
//先插入元素,然后将栈顶指针加 1
*(s.top) = e;
s.top++;
return OK;
}
栈底数据出
我想大家可以从上面的小标题就可以看出来这一小节讲的什么。因为我们开始说过栈的特性的先进后出。那么我们要完成栈底数据出的话就要像入数据一样先判断,但这里是判断是否为空,因为我要出数据要有吧。不然这拿不出东西不见完了嘛。
//出栈
Status Pop(SqStack &s,SDataType &e){
//判断是否会发生下溢
if(s.top != s.base){
s.top--; //先将栈顶指针减 1
e = *(s.top);
}else{
return 0;
}
return e;
}
总结
以上就是栈的基本几个实现了。然后下面的栈代码的实现了。大家可以自取。
#include<stdio.h>
#include<malloc.h>
#define STACK_INIT_SIZE 100 //栈的初始容量
#define STACKINCREMENT 10 //容量增量
#define OK 1
#define OVERFLOW -2
typedef int SDataType;
typedef int Status;
typedef struct{
SDataType *base; //栈底指针
SDataType *top; //栈顶指针
int StackSize; //当前已经分配的存储空间,以元素为单位
}SqStack;
//初始化顺序栈,构造一个空栈
Status InitStack(SqStack &S){
//分配存储空间
S.base = (SDataType *)malloc(STACK_INIT_SIZE*sizeof(SDataType));
if(!S.base){
//如果分配失败,则返回error
return OVERFLOW;
}
//S.top 始终指向栈顶元素的下一个位置
S.top = S.base; //初始状态下为空栈
S.StackSize = STACK_INIT_SIZE; //当前已经分配的存储容量为100个
return OK;
}
//入栈
Status Push(SqStack &s,SDataType e){
SDataType *p;
//首先判断栈是不是满的(上溢)
if(s.top-s.base == s.StackSize){
//追加空间
p = (SDataType *)realloc(s.base,(s.StackSize + STACKINCREMENT)*sizeof(SDataType));
if(!p){
//如果没有找到符合条件的存储空间,则返回error
return OVERFLOW;
}
//成功找到则使s.base指向p
s.base = p; //系统会将原来的内容复制过来
s.top = s.base + s.StackSize;
s.StackSize += STACKINCREMENT;
}
//先插入元素,然后使栈顶指针加 1
*(s.top) = e;
s.top++;
return OK;
}
//出栈
Status Pop(SqStack &s,SDataType &e){
//判断是否会发生下溢
if(s.top != s.base){
s.top--; //先将栈顶指针减 1
e = *(s.top);
}else{
return 0;
}
return e;
}
//判断是否为空栈
void judgeNull(SqStack &s){
if(s.top == s.base){
printf("此栈为空栈!\n");
}else{
printf("此栈不为空栈!\n");
}
}
//判断是否为满栈
void judgeFull(SqStack &s){
if(s.top-s.base == s.StackSize){
printf("栈满!\n");
}else{
printf("栈未满!\n");
}
}
int main(){
SqStack s;
SDataType element;
InitStack(s); //初始化栈
//将1-10入栈
for(int i=1;i<=10;i++){
Push(s,i);
}
judgeNull(s);
judgeFull(s);
printf("出栈:\n");
//只要栈不为空
while(s.top != s.base){
Pop(s,element); //出栈的元素用e接收
printf("%d ",element);
}
printf("\n");
judgeNull(s);
return 0;
}