目录
一.🚀前言🚀
前面我们讲解了顺序表,单链表以及双链表,接下来我们将进入另一种数据结构的学习---栈。注意,这里说的栈是数据结构的一种,而不要和栈区混淆。栈是一种数据结构,而栈区是内存中划分的一个区域。
二.🚀栈🚀
1.💠栈的概念💠
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。
2.💠压栈💠
栈的插入操作叫做进栈,压栈或入栈。压栈时,数据压在栈顶。
3.💠出栈💠
栈的删除操作叫做出栈。出数据时同样是在栈顶出。
4.💠栈的特性💠
栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。先压入栈中的数据后出栈。
三.🚀栈内各函数的实现🚀
本章栈是以顺序表为基础实现的,顺讯表即数组
1.💠定义栈💠
//数组栈
//定义类型
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}Stack;
栈由顺序表实现,因此需要一个数组,而栈有容量大小以及需要获取栈顶数据,所以需要同时管理多种不同类型数据,进而用一个结构体进行管理刚刚好。typedef将数据类型int以及结构体进行了重定义,以便于后续书写及修改。
2.💠栈的初始化💠
//初始化栈
void STInit(Stack* pst)
{
assert(pst);
pst->a = NULL;
pst->capacity = 0;
pst->top = 0;
}
assert对栈地址进行断言,防止传参时失误传为空指针,然后将数组a置为空,容量以及栈顶赋初值为0。严格来说,这里的top其实并不是指向栈顶的,而是指向栈顶的下一个位置,因为当进行压栈操作时,需要往top位置放入数据,而放入数据后需要将top++,以便于后续压栈,因此top实际上是栈顶元素的下一个位置。
3.💠销毁栈💠
//销毁栈
void STDestroy(Stack* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->capacity = 0;
pst->top = 0;//top是栈顶数据的下一个位置
}
栈的销毁和栈的初始化及其相似,不同之处在于栈的销毁需要将数组的空间进行释放,其余操作都一样。数组a是动态内存开辟出来的,因此需要用free进行释放,以免造成内存泄漏。数组a是如何开辟出来的后面会进行介绍。
4.💠压栈💠
//压栈
void STPush(Stack* pst, STDataType x)
{
assert(pst);
if (pst->top == pst->capacity)
{
int newcapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;
STDataType* tmp = (STDataType*)realloc(pst->a, newcapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
return;
}
pst->a = tmp;
pst->capacity = newcapacity;
}
pst->a[pst->top] = x;
pst->top++;
}
在进行压栈前,都必须先对栈的容量进行判断,而一旦栈的容量已满就需要考虑进行扩容,这样才会有足够多的空间压栈。
很多小伙伴在之前初始化的时候或许就有这么一个疑惑,初始化的时候不应该起码开辟一个带有一定空间大小的数组吗?要不然刚开始的时候那些数据放在哪儿?
其实,这是博主埋的一个伏笔,这就是上面这个代码的奇妙之处,用一个三目操作符以及realloc动态内存函数就很好的解决了这一问题,初始化时我们将top以及capacity和a都初始化为了空。因此当我们插入数据时,第一次进来就会开辟数组的基础大小,第二次进来满足条件才会进行扩容。这里值得一说的是,realloc在第一个参数即pst->a为空时,其功能和malloc是一样的,所以这段代码很好的解决了栈的初始大小开辟以及扩容的问题。
5.💠出栈💠
//出栈
void STPop(Stack* pst)
{
assert(pst);
assert(!STEmpty(pst));
pst->top--;
}
出栈非常简单,前面说过,出栈出的是栈顶元素,因此主需要将top--即可。出栈其实就是删除栈顶元素,栈内实际有效数据其实就是下标0到top - 1这个范围,因此当将top--后,栈的实际有效数据范围也就减小了,栈顶元素也就相当于被删除了。STEmpty(pst)为判空函数,如何实现后面介绍,这里主要是预防当栈为空后,继续对栈进行删除。
6.💠判空💠
//判空
bool STEmpty(Stack* pst)
{
assert(pst);
return pst->top == 0;
}
bool是一种数据类型,简单来说就是用bool做桉树返回类型时,函数可以返回true或是false。如果栈为空返回true,不为空返回false。当top为0时,pst->top == 0表达式结果为真,函数返回true,否则返回false。
7.💠获取栈顶元素💠
//获取栈顶数据
STDataType STTop(Stack* pst)
{
assert(pst);
assert(!STEmpty(pst));
return pst->a[pst->top - 1];
}
同样栈为空时,无法获取栈顶元素。先前说过,top其实是栈顶元素的下一个位置,所以想要获取栈顶数据,还需将top减1才行。
8.💠获取栈内元素个数💠
//栈内元素个数
int STSize(Stack* pst)
{
assert(pst);
return pst->top;
}
我们知道数组的下标是从0开始的,而top是栈顶元素的下一个位置,刚好就是栈内元素个数的大小。
四.🚀栈的整体实现🚀
1.💠Stack.h💠
包含头文件及类型和函数的声明
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//数组栈
//定义类型
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}Stack;
//初始化栈
void STInit(Stack* pst);
//销毁栈
void STDestroy(Stack* pst);
//压栈
void STPush(Stack* pst, STDataType x);
//出栈
void STPop(Stack* pst);
//判空
bool STEmpty(Stack* pst);
//获取栈顶数据
STDataType STTop(Stack* pst);
//栈内元素个数
int STSize(Stack* pst);
2.💠Stack.c💠
函数功能实现
#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"
//初始化栈
void STInit(Stack* pst)
{
assert(pst);
pst->a = NULL;
pst->capacity = 0;
pst->top = 0;
}
//销毁栈
void STDestroy(Stack* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->capacity = 0;
pst->top = 0;//top是栈顶数据的下一个位置
}
//压栈
void STPush(Stack* pst, STDataType x)
{
assert(pst);
if (pst->top == pst->capacity)
{
int newcapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;
STDataType* tmp = (STDataType*)realloc(pst->a, newcapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
return;
}
pst->a = tmp;
pst->capacity = newcapacity;
}
pst->a[pst->top] = x;
pst->top++;
}
//出栈
void STPop(Stack* pst)
{
assert(pst);
assert(!STEmpty(pst));
pst->top--;
}
//判空
bool STEmpty(Stack* pst)
{
assert(pst);
return pst->top == 0;
}
//获取栈顶数据
STDataType STTop(Stack* pst)
{
assert(pst);
assert(!STEmpty(pst));
return pst->a[pst->top - 1];
}
//栈内元素个数
int STSize(Stack* pst)
{
assert(pst);
return pst->top;
}
3.💠test.c💠
测试函数逻辑及功能
#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"
void TestStack()
{
Stack st;
STInit(&st);
STPush(&st, 1);
STPush(&st, 2);
STPush(&st, 3);
STPush(&st, 4);
STPush(&st, 5);
STPop(&st);
STPop(&st);
printf("Size:%d", STSize(&st));
printf("\n");
while (!STEmpty(&st))
{
printf("%d ", STTop(&st));
STPop(&st);
}
STDestroy(&st);
}
int main()
{
TestStack();
return 0;
}
本篇以栈的实现为主题的博客到此就要结束了,希望本篇博客可以帮助到各位小伙伴,博主码字不易,如果本篇博客对你有所帮助,还望各位小伙伴点赞👍,收藏⭐+关注,感谢各位的支持!如果有什么不明白的地方或是另有其他的高见,也可以在评论区留言,博主也会及时回复或进行更改,欢迎指正,谢谢!