大家好呀,今天我们先来认识一下栈。
栈与队列
目录
栈
我们先来认识栈。什么是栈呢?
栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。如图:
出栈:栈的删除操作叫做出栈。出数据也在栈顶。如图:
栈讲究的是先进后出的原则,也就是说,栈顶的部分的插入和删除需要较为方便。相比于链表,用数组,也就是顺序表来实现栈则更为的方便,那我们就来对栈进行实现。
创建栈
那么栈需要使用结构体包含成员吗?当然,栈的元素一般都不止一个,因此我们创建结构体来包含其元素。代码如下:
typedef struct Stack { int* a; int top; //标识栈顶位置 int capacity; //检查容量 }ST;
那么首要的,便是将栈进行初始化,那我们就先来编写初始化和销毁函数。
初始化和销毁
先编写初始化函数。
初始化我们都很熟悉了,将指针置空,容量置0就可以了。要注意的是,栈顶位置的赋值需要进行思考,因为如果赋值为0的话,当top==0时。区分栈只有一个元素还是栈为空较为困难,所以可以选择将top的值赋值为-1。我们这里还是选择将其赋值为0。 代码如下:
void STInit(ST* pst) { assert(pst); pst->a = NULL; pst->top = 0; pst->capacity = 0; }
接着便是销毁函数。
销毁的话进行置空和free就可以了。代码如下:
void Destroy(ST* pst) { assert(pst); free(pst->a); pst->a = NULL; pst->top = pst->capacity = 0; }
入栈和出栈
接下来便是让元素入栈和出栈,相当于尾插和尾删。
入栈函数,首要的便是在入栈前检查容量是否足够,不够,则进行扩容。接下来我们来编写入栈。入栈,只需要将数组的尾部,也就是栈顶插入新的数据即可。 插入完成后将top的位置进行更改。代码如下:
void STPush(ST* pst, STDataType x) { assert(pst); if (pst->top == pst->capacity) { int newcapacity = pst->capacity == 0 ? 4 : (pst->capacity) * 2; STDataType* tmp = realloc(pst->a, sizeof(STDataType) * newcapacity); if (tmp == NULL) { perror("malloc fail"); exit(-1); } pst->a = tmp; pst->capacity = newcapacity; } pst->a[pst->top] = x; pst->top++; }
那么出栈,也就是尾删,就过于简单了,直接让top--即可,当然top为0时就不能减少了。需要判断。代码如下:
void STPop(ST* pst) { assert(pst); assert(pst->top > 0); pst->top--; }
访问栈顶的数据
接下来便是访问栈顶的数据,直接进行访问即可,要注意的时,因为将top置为0,我们访问top相当于访问栈顶元素的下一个,因此访问栈顶元素需要-1。代码如下:
STDataType STTop(ST* pst) { assert(pst); assert(pst->top > 0); return pst->a[pst->top - 1]; }
检查栈是否为空
检查栈是否为空也是比较简单的,我们是将top初始化为0,因此当top为0的时候,栈就空了。代码如下:
bool STEmpty(ST* pst) { if (pst->top == 0) { return true; } else { return false; } }
计算元素个数
计算栈元素个数也较为简单,top的个数就是栈元素的个数。代码如下:
int STSize(ST* pst) { assert(pst); return pst->top; }
检查打印
当然,我们最后还是来检查一下,编写是否存在错误。我们让1,2,3,4依次入栈,访问完栈顶元素后进行出栈。代码如下:
int main() { ST s; STInit(&s); STPush(&s, 1); STPush(&s, 2); STPush(&s, 3); STPush(&s, 4); while (!STEmpty(&s)) { printf("%d ", STTop(&s)); STPop(&s); } printf("\n"); return 0; }
运行结果如下:
完整代码
stack.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top; //标识栈顶位置
int capacity; //检查容量
}ST;
//初始化
void STInit(ST* pst);
//销毁
void Destroy(ST* pst);
//入栈
void STPush(ST* pst, STDataType x);
//出栈
void STPop(ST* pst);
//访问栈顶数据
STDataType STTop(ST* pst);
//检查栈是否为空
bool STEmpty(ST* pst);
//元素个数
int STSize(ST* pst);
stack.c
#include"stack.h"
//初始化
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
//入栈
void STPush(ST* pst, STDataType x)
{
assert(pst);
if (pst->top == pst->capacity)
{
int newcapacity = pst->capacity == 0 ? 4 : (pst->capacity) * 2;
STDataType* tmp = realloc(pst->a, sizeof(STDataType) * newcapacity);
if (tmp == NULL)
{
perror("malloc fail");
exit(-1);
}
pst->a = tmp;
pst->capacity = newcapacity;
}
pst->a[pst->top] = x;
pst->top++;
}
//出栈
void STPop(ST* pst)
{
assert(pst);
assert(pst->top > 0);
pst->top--;
}
//访问栈顶数据
STDataType STTop(ST* pst)
{
assert(pst);
assert(pst->top > 0);
return pst->a[pst->top - 1];
}
//检查栈是否为空
bool STEmpty(ST* pst)
{
if (pst->top == 0)
{
return true;
}
else
{
return false;
}
}
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
//销毁
void Destroy(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->top = pst->capacity = 0;
}
test.c
#include"stack.h"
int main()
{
ST s;
STInit(&s);
STPush(&s, 1);
STPush(&s, 2);
STPush(&s, 3);
STPush(&s, 4);
while (!STEmpty(&s))
{
printf("%d ", STTop(&s));
STPop(&s);
}
printf("\n");
return 0;
}
小结
对栈的认识到这里就结束啦,队列也会很快端上来的,我们下次见!