今天船长计划用简单的方式向大家介绍数据结构中重要知识点——栈
此篇文章中,我将带领大家一步一步用C语言将栈结构给实现出来;全文干货,强烈建议观众老爷们先赞后看哦!
目录
1.栈的概念及结构
栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。
进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶;
出栈:栈的删除操作叫做出栈。出数据也在栈顶;
栈的进栈和出栈具体形式如下图所示:
2.栈的实现
栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。
原因:
- 因为数组在尾上插入数据的代价比较小;
- 数组连续储存在栈区,相比堆区缓存更加友好;
- 另外数组一般属于高级语言语法中的基本类型,结构简单,有利于编译器做寄存器优化、常量折叠、无用代码精简等,优化完速度还要更快。
2.1头文件的包含
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>//对应下文验空函数
2.2构造结构体
栈结构分为静态结构和动态结构,现实中动态结构更为常用,因此我们这次主要实现栈的动态结构
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
2.3定义相应函数
//初始化
void StackInit(ST* ps);
//销毁
void StackDestory(ST* ps);
//入栈
void StackPush(ST* ps, STDataType x);
//出栈
void StackPop(ST* ps);
//获取头部
STDataType StackTop(ST* ps);
//获取号数
int StackSize(ST* ps);
//验空
bool StackEmpty(ST* ps);
2.3实现相应函数
初始化、销毁
初始化主要是将指针置空,数据归零;
销毁的步骤也和初始化差不多,但是在此之前要多一步:释放空间,解除占用
//初始化
void StackInit(ST* ps)
{
assert(ps);//检查*ps是否为空指针
ps->a = NULL;
ps->capacity = ps->top = 0;
}
//销毁
void StackDestory(ST* ps)
{
assert(ps);
free(ps->a);//释放储存数据的空间
ps->a = NULL;
ps->capacity = ps->top = 0;
}
入栈、出栈
入栈的实现要比出栈复杂,主要是因为在进行数据入栈时,要检查栈的空间是否足够,如果不够则要申请新的空间,在C语言的实现中我们通常使用realloc函数(stdlib.h)实现,然后我们还要查验新申请空间的指向针针是否有问题,再置换指针和空间数据,存入数据
//入栈
void StackPush(ST* ps, STDataType x)
{
assert(ps);
//检查容量
if (ps->capacity == ps->top)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
//ps->a指向需要重新分配内存的的内存块
//newcapacity * sizeof(STDataType)是新空间的字节数
STDataType* cur = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
//验证申请的空间有没有问题
if (cur == NULL)
{
perror("realloc fail");
exit(-1);//失败即退出程序
}
//还要把新指针给a,新空间给capacity
ps->a = cur;
ps->capacity = newcapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
出栈的结构相比于入栈可谓是十分简单了,可也有一些要注意的点:
- 要验证栈中是否为空,如若为空,则无法进行出栈;
- 这里出栈不需要释放空间;
//获取数据都要检验,数据是否为空
//出栈
void StackPop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));//验证栈是否为空,如果为空,则无法出栈
//不用释放空间
//free(ps->a[ps->top]);
ps->top--;
}
获取栈顶、获取栈顶对应号数
获取栈顶和出栈一样同样要验证栈中是否为空,同时返回栈顶对应数,这里就是ps->a[ps->top-1]
因为我的top是从0开始存的,所以按照数组的性质,要top-1
//获取栈顶
STDataType StackTop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));//若无数据,无法获取栈顶
return ps->a[ps->top-1];
}
//获取栈顶号数
int StackSize(ST* ps)
{
assert(ps);
return ps->top;
}
验空函数
通过封装一个独立的验空函数,就可以让多个函数同时应用,减少了代码的冗余,保持了代码的整洁。当然,验空所具有的作用也不可替代。
Tip:如果你已经提前定义了函数,在实现过程中即使放在最后完成也是可以的哦~
//验空
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
3.结语
看到这里,相信老铁们对如何用C语言实现栈已经有具体的认识。
我是计算机海洋的新进船长Captain_ldx,如果我的文章能对您有帮助的话,麻烦各位观众姥爷们点赞、收藏、关注,你们的每一次举手之劳都将化为船长的前进动力!