目录
ID:HL_5461
引言
(前方超短提示~这篇真没啥可说的……)
先来看看栈是什么。
栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
后进:
先出:
一、定义
本篇我们选择使用数组作为栈,这又回到了之前所讲过的顺序表,不太理解的可以去翻我之前写的博客:【线性表】顺序表详解(C语言版)_是兰兰呀~的博客-优快云博客
在实现栈的过程中,我们使用动态的顺序表,这样方便对其进行扩容。不过与顺序表定义不同的一点是,我们将不再使用size,而是改用top,top代表栈顶元素下标,其余,指针a,空间capacity都不变化。
前面我们说了,top代表栈顶元素下标,这样就面临一个问题:下标指向哪比较合适?
如图,我们该选择top1还是top2作为top呢?这其实就是一个习惯问题。当我们选择top1时,栈内无元素时top等于0,此时top与顺序表的size含义相同,代表栈内元素个数,a[top]为栈顶元素的后一位;当我们选择top2时,a[top]表示栈顶元素,这时要注意,当栈内无元素时top为-1。
一方面为了和顺序表保持一致,另一方面也是个人习惯,我选择top1作为top。
我们将存储的数据类型的指针a,栈顶元素下标top,以及表示当前开辟的空间大小的capicity封装到一个结构体中,如下图所示:
typedef int STDataType;//定义STDataType为int,方便以后修改存储的数据类型
typedef struct Stack
{
STDataType* a;//动态开辟的空间指针
int top;//栈顶元素下标
int capacity;//空间大小
}ST;
二、初始化
在顺序表中,我们在初始化时就顺便开辟了四个大小为SLDataType的空间,但在栈中,我们将开辟空间统一放到插入中去,初始化只做最简单的置空指针以及将top和capacity置0。
void STInit(ST* ps)
{
assert(ps);//ps不为空指针
ps->a = NULL;//数组指针置空
ps->top = 0;//栈顶元素下标为0
ps->capacity = 0;//空间大小为0
}
三、销毁
栈的销毁和顺序表一样,三步走:释放空间、指针置空、变量置零。OK,讲完,上代码~
void STDestroy(ST* ps)
{
assert(ps);//ps不为空指针
free(ps->a);//释放数组
ps->a = NULL;//置空数组指针
ps->top = 0;//栈顶元素下标置0
ps->capacity = 0;//空间大小置0
}
四、插入
栈的插入就是顺序表的尾插,由于栈只允许在固定的一端进行插入和删除元素操作的特点,它没有诸如头插、中间插入等花里胡哨的东西。同样,也是因为这个,我们不需要跟顺序表一样特意写一个容量判断的函数了,将这部分功能直接放到插入里面也是一样的。
我们先判断栈空栈满,当top == capacity时栈即为满,栈满需扩容。
扩容其实并不难,我们照例让空间变为原来的两倍,但由于我们并没有像之前一样初始化分配4个空间,所以这里存在capacity为0的特殊情况:0*0 = 0,。我们还需有一个对capacity的判断,当它为0时,为它分配4个STDataType大小的空间,不为0时令新空间大小为原先的二倍。
将x值放入新top所在位置,并将top++。
void STPush(ST* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)//栈满
{
int NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//原空间为0令新空间大小为4,否则扩为2倍
STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * NewCapacity);//为原空间扩容
if (tmp == NULL)//扩容失败
{
perror("STPush");//打印失败
exit(-1);//程序以非正常形式结束
}
//扩容成功
ps->a = tmp;//将扩容后的地址赋给a
ps->capacity = NewCapacity;//修改capacity为新大小
}
ps->a[ps->top] = x;//将x放入栈顶
ps->top++;//栈顶元素下标加1
}
五、删除
so easy~这个不讲了哈~就是尾删,直接top--就OK了,注意断言栈不为空就行了。
void STPop(ST* ps)
{
assert(ps);//ps不为空
assert(ps->top > 0);//栈不为空
ps->top--;//栈顶元素下标少1
}
六、获取栈顶元素
为了体现栈后进先出的特性,栈一般是没有打印、查找等操作的,但有个获取栈顶元素的操作,这个操作一般和删除连用,读一个删一个,这样就可以依次读出后来进入的元素啦~
当然这个操作本身还是超简单的,直接返回a[top-1]即栈顶元素就好。
STDataType STTop(ST* ps)
{
assert(ps);//ps不为空
assert(ps->top > 0);//栈不为空
return ps->a[ps->top - 1];
}
七、判空
我其实挺纠结这个要不要放,真的太简单了,但还是争取做到面面俱到吧……
这个函数的返回值我们设为bool类型,空返回真,非空返回假。
bool STEmpty(ST* ps)
{
assert(ps);//ps不为空
return ps->top == 0;//top为0则空,不为0则非空
}
八、已存放数据数
正如我们开头讲的,这些代码的top其实就相当于顺序表里的size,代表的就是已存放的数据数,so,直接返回top值就行。
int STSize(ST* ps)
{
assert(ps);//ps不为空
return ps->top;//top即为已存放数据数
}
结尾
emmm…目录看着似乎挺多操作,但这篇真没啥可说的,所以,就这样叭~下次再见~
这是本篇代码码云链接:
若有错误,欢迎大家批评斧正!