一.概念
- 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵循后进先出的原则(就和子弹放入弹夹时原理相同)
- 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶
- 出栈:栈的删除操作叫做出栈,出数据也在栈顶
如图:入栈和出栈的过程
二.关于栈的选择
栈也是有两种结构
- 顺序栈:我本人更习惯这种栈,实现比较简单,第一个元素作为栈底,栈顶慢慢增加(这里为什么top = 4 ,因为,先赋值,top再++)
- 链栈:基于链表的特点,我们就得把头节点作为栈的栈顶,链栈在出栈进栈时,没有顺序栈方便
三.顺序栈的实现
首先在这里声明,文中写的是动态顺序栈,就需要我们用到动态顺序表实现中的扩容函数
1.定义一个栈
typedef int STDataType; //定义STDataType为int型,方便更改栈中元素类型
typedef struct Stack
{
STDataType* a;
int top; //记录栈顶
int capacity; //容量(可以直接调用扩容函数去扩容)
}ST;
2.栈的初始化
注意:
这里我令 ps->top = 0 是在存入数据中,先放元素,再++
也可以令 ps->top = -1 是先++,再存入数据
(这两种写法都可以)
//初始化栈
void StackInit(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->top = 0; //先放再++
//ps->top = -1; 是先++再放 这两种写法都可以
ps->capacity = 0;
}
3.入栈
在栈中只有入栈才能用到扩容,所以判断是否扩容与入栈写在一起
//入栈
void StackPush(ST* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity) //这里判断容量够不够,与动态顺序表中的使用一样
{
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);
if (tmp == NULL) //判断malloc是否成功
{
printf("reallos false\n");
exit(-1);
}
ps->a = tmp;
ps->capacity = newCapacity;
}
ps->a[ps->top] = x; //x是入栈元素,要符合先进后出规则,进来一个元素,这个元素 就是栈顶
ps->top++;
}
4.出栈
出栈就比较简单了(先进后出)
//出栈
void StackPop(ST* ps)
{
assert(ps);
assert(ps->top > 0); //要出栈,栈不能为NULL,后面要写判断栈是否是空的函数,可以直接调用
//assert(!StackEmpty(ps)); 后面要写的判断栈是否为空的函数
ps->top--;
}
5.栈的大小
//栈的大小
int StackSize(ST* ps)
{
assert(ps); //断言,好习惯
return ps->top; //这里直接返回 ps->top 因为,数组下标从零开始,而 ps->top 是先赋值再++,所以栈的大小,就是 ps->top
}
6.判断栈是否为空
这里用到了bool,需要引用头文件 #include<stdbool.h>
是用来判断 ps->top == 0 的,如果等于0,返回真,否则,为假
(注意:要在别的函数中用到这个函数,要把这个函数放到那个函数前面)
//判断栈是否为空
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0; // == 返回的就是逻辑真,逻辑假
}
7.释放空间
//释放栈
void StackDestroy(ST* ps)
{
assert(ps);
free(ps->a); //释放空间
ps->a = NULL;
ps->capacity = ps->top = 0; //都置为0
}
总结
- 栈,总体来看是比较简单的,如果动态顺序表很熟练,这个栈就会很好写
- 记住栈的特点,先进后出,不能中间插入(子弹入弹夹)
- assert的作用很大,能帮助我们很好找到错误的地方,要习惯使用
全部代码
#include<stdio.h>
#include<assert.h>
#include<stdbool.h>
#include<stdlib.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top; //记录栈顶
int capacity; //容量(可以直接调用扩容函数去扩容)
}ST;
//初始化栈
void StackInit(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->top = 0; //先放再++
//ps->top = -1; 是先++再放 这两种写法都可以
ps->capacity = 0;
}
//判断栈是否为空
bool StackEmpty(ST* ps)
{
assert(ps);
/*if (ps->top == 0)
{
return true;
}
else
{
return false;
}*/
return ps->top == 0; // == 返回的就是逻辑真,逻辑假
}
//释放栈
void StackDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
//入栈
void StackPush(ST* ps, STDataType x)
{
assert(ps);
//判断容量,增容
if (ps->top == ps->capacity)
{
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);
if (tmp == NULL)
{
printf("reallos false\n");
exit(-1);
}
ps->a = tmp;
ps->capacity = newCapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
//出栈
void StackPop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps)); //assert(ps->top > 0);
ps->top--;
}
//栈顶元素
STDataType StackTop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps)); //assert(ps->top > 0);
return ps->a[ps->top - 1];
}
//栈的大小
int StackSize(ST* ps)
{
assert(ps);
return ps->top;
}
int main()
{
ST st;
StackInit(&st); //初始化栈
StackPush(&st, 1); //入栈
StackPush(&st, 2);
StackPush(&st, 3);
StackPush(&st, 4);
int len = StackSize(&st); //栈的大小
printf("%d\n", len);
while (!StackEmpty(&st))
{
printf("%d ", StackTop(&st)); //边打印栈顶,边出栈
StackPop(&st);
}
StackDestroy(&st); //释放空间
return 0;
}
感谢大家的观看,希望你能从这篇文章中学到一些东西(如有错误,提醒我,我会及时修改)
谢谢大家!!!