目录
上节我们讲解了双向循环链表,这节开始学习栈!
栈的概念
栈是一个特殊的线性表,只能在一端操作;
栈顶(top):允许操作的一端;
栈底(bottom):不允许操作的一端
特点是:最先进栈的最后出栈。
通俗来讲,最先进去的被压在最下面,所以只能最后才出来。
实现栈有两种方式:顺序存储(顺序栈),链式存储(链栈)
即我们可以用数组来实现,也可以用链表来实现。
本节先来讲用数组来存放栈结构。
栈顶我们一般需要一个栈顶指针,但是数组没有指针,只有下标
此时栈顶3的下标就是2,栈底1的下标是0。
当我们初始化一个空栈的时候,栈顶指针(下标)应该设置为-1,如果设置为0的话就还不能表示空栈。
我们在程序上如果来表示一个栈呢?
我们还是用结构体来表示:
struct Stack
{
int*data;//指向栈所在的内存
int top;//栈顶
}
只要知道栈顶在哪了,就知道这个栈的容量有多大了。
接下来用代码来实现栈的一些操作
先创建一个目录seqStack
定义顺序栈
创建一个stack.h文件
在这个头文件里面定义一个栈和栈的容量,以及成功/失败的宏定义
然后main.c中创建一个栈
栈的初始化
Stack.c
在stack.h中声明一下这个函数
Main.c
运行结果:
进栈操作
代码演示:
Stack.c
栈的结构体中,我们可以把data看成是数组名,top是下标
在stack.h中声明一下这个函数
Main.c
运行结果:
判断栈是否为空
Stack.c
在stack.h中声明一下这个函数
Main.c
运行结果
获取栈顶元素
注意栈顶元素的值是否正好是1000(SUCCESS)或者1001(FAILURE),我们这里定义的随机数是0~20,随机不存在这个冲突的问题,以后再开发中可能需要注意。
代码演示:
Stack.c
在stack.h中声明一下这个函数
Main.c
运行结果:
出栈操作
出栈时需要返回出栈的元素,top向下移动
Stack.c
在stack.h声明一下这个函数
Main.c
运行结果
注意:栈是不存在遍历操作的。
清空栈操作
因为顺序栈的内存是连续的,所以清空操作只需要让top等于-1表示空栈就行了。
Stack.c
在stack.h声明一下这个函数
Main.c
运行结果:
清空成功后我们可以获取栈顶元素验证一下是否真的清空了
运行结果
的确清空了
销毁栈操作
Stack.c
在stack.h声明一下这个函数
Main.c
运行结果
按理来说销毁栈时候就不能进栈了,所以可以调用一个进栈的代码验证一下是否真的销毁成功
运行结果
出现了段错误,是因为销毁的时候,data置为了空指针,但是进栈的时候我们只判断了s是否为空指针,所以我们还需要在进栈时候判断一些data是否为空指针
再次运行
的确销毁成功了。
完整代码
Stack.c
#include "stack.h"
#include <stdlib.h>//NULL的头文件
//栈的初始化
int InitStack(Stack*s)
{
//入参判断
if(NULL==s)
{
return FAILURE;
}
s->data=(int*)malloc(sizeof(int)*SIZE);//申请空间
if(NULL==s->data)
{
return FAILURE;//申请失败
}
s->top=-1;//栈顶初始化为-1
return SUCCESS;
}
//进栈操作
int PushStack(Stack *s,int num)
{
if(NULL==s||NULL==s->data)
{
return FAILURE;
}
//判断栈是否已满
if(s->top>=SIZE-1)
{
return FAILURE;
}
s->data[++s->top]=num;//top先作为下标移动到第一个位置,然后再放入num
return SUCCESS;
}
//判断栈是否为空
int EmptyStack(Stack s)
{
return (s.top==-1)? SUCCESS:FAILURE;
}
//获取栈顶元素
int GetTop(Stack s)
{
return (s.top==-1)? FAILURE:s.data[s.top];
}
//出栈操作
int PopStack(Stack *s)
{
if(NULL==s)
return FAILURE;
//判断栈是否为空
if(s->top==-1)
return FAILURE;
return s->data[s->top--];//先返回,top再减减
}
//清空操作
int ClearStack(Stack*s)
{
if(NULL==s)
return FAILURE;
s->top=-1;
return SUCCESS;
}
//销毁操作
int DestroyStack(Stack*s)
{
if(NULL==s)
return FAILURE;
free(s->data);
//释放掉后data成为野指针,所以要置为空指针
s->data=NULL;
return SUCCESS;
}
Stack.h
#ifndef _STACK_H
#define _STACK_H
#define SIZE 10 //栈的容量
#define SUCCESS 1000
#define FAILURE 1001
//表示栈的结构体
typedef struct seqStack
{
int *data;
int top;
}Stack;
int InitStack(Stack*s);
int PushStack(Stack *s,int num);
int EmptyStack(Stack s);
int GetTop(Stack s);
int PopStack(Stack *s);
int ClearStack(Stack*s);
int DestroyStack(Stack*s);
#endif
Main.c
#include "stack.h"
#include <stdio.h>
#include <time.h>//随机数头文件
#include <stdlib.h>
int main()
{
//创建栈
Stack s;
//栈的初始化
int ret=InitStack(&s);
if(SUCCESS==ret)
{
printf("栈初始化成功\n");
}
else
{
printf("栈初始化失败\n");
}
//进栈操作
int i,num;
srand(time(NULL));
for(i=0;i<5;i++)//进5个
{
num=rand()%20;
ret=PushStack(&s,num);//因为等会儿要改栈顶,所以传s的地址
if(SUCCESS==ret)
{
printf("%d 进栈成功\n",num);
}
else
{
printf("%d 进栈失败\n",num);//栈满了
}
}
//判断栈是否为空
ret=EmptyStack(s);
if(ret==SUCCESS)
{
printf("栈为空\n");
}
else
{
printf("栈不为空\n");
}
//获取栈顶元素
ret=GetTop(s);
if(FAILURE==ret)
{
printf("不存在栈顶元素\n");
}
else
{
printf("栈顶元素是%d\n", ret);
}
//出栈操作
for(i=0;i<5;i++)//出栈5个元素
{
ret=PopStack(&s);//一会儿要改top,所以取地址
if(FAILURE==ret)
{
printf("出栈失败\n");
}
else
{
printf("%d 出栈成功\n",ret);
}
}
//清空操作
ret=ClearStack(&s);
if(SUCCESS==ret)
{
printf("清空成功\n");
}
else
{
printf("清空失败\n");
}
//获取栈顶元素,验证是否清空了
ret=GetTop(s);
if(FAILURE==ret)
{
printf("不存在栈顶元素\n");
}
else
{
printf("栈顶元素是%d\n", ret);
}
//销毁操作
ret=DestroyStack(&s);//释放掉data指向的空间后要修改为NULL,所以需要传地址
if(SUCCESS==ret)
{
printf("销毁成功\n");
}
else
{
printf("销毁失败\n");
}
//进栈操作,验证是否真的销毁成功
srand(time(NULL));
for(i=0;i<5;i++)//进5个
{
num=rand()%20;
ret=PushStack(&s,num);//因为等会儿要改栈顶,所以传s的地址
if(SUCCESS==ret)
{
printf("%d 进栈成功\n",num);
}
else
{
printf("%d 进栈失败\n",num);//栈满了
}
}
return 0;
}
顺序栈我们就介绍完毕了,下节开始讲链栈!
QQ交流群:963138186
本篇就到这里,下篇继续!欢迎点击下方订阅本专栏↓↓↓