可以先了解线性表的无头单向非循环链表(单链表)---数据结构——单链表的增加、删除、查找、修改,详细解析_昵称就是昵称吧的博客-优快云博客,和线性表的带头双向循环链表---数据结构:带头双向循环链表——增加、删除、查找、修改,详细解析_昵称就是昵称吧的博客-优快云博客
目录
一、线性表之栈
1、栈的表示和实现
1.1栈的概念及结构
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除。
操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈,出数据也在栈顶。
可以将栈理解为弹夹,压栈就是压入子弹,出栈就是打出子弹。
结构如下所示:
1.2栈的实现表示方法
栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。
2、设计栈的功能实现
2.1栈空间通过动态存储数据
typedef int StackDataType;//重定义类型名
typedef struct Stack
{
StackDataType* a;//创建一个指针,用来接收数组首元素地址,数组里面的类型是StackDataType
//用数组来描述栈,因为出栈和入栈都是一个地方
int top;//相当于数组的下标,可以是0或-1,这里我们选择0
int capacity;//数组的容量
}ST;
结构体里用一个指针接收动态开辟的内存空间的地址。
2.2栈空间的初始化
//初始化结构体,将结构体变量的地址传递过来,是地址传递,这样就能改变实参的内容了
void StackInit(ST* ps)
{
assert(ps);//地址传递,断言好习惯
//动态开辟一个数组的空间,数组的元素类型是StackDataType,将首元素地址赋给指针a
ps->a = (StackDataType*)malloc(sizeof(StackDataType) * 4);
if (ps->a == NULL)//判断是否成功开辟动态空间
{
perror("the mistake is");
}
ps->top = 0; //相当于数组下标,可以初始化0或-1,这里用0
//用0数组下标从0开始,用-1数组下标从-1开始
ps->capacity = 4; //从开辟的动态空间可以看出数组的容量是4
}
通过地址传递,初始化结构体里的内容。
2.3栈空间的销毁
//销毁结构体里的内容,地址传递,才能改变实参的内容
void StackDestroy(ST* ps)
{
assert(ps);
free(ps->a);//释放动态开辟的内存
ps->a = NULL;
ps->top = 0; //将结构体里的 相当于数组下标top 和 容量capacity 置为0
ps->capacity = 0;
}
通过释放函数free释放动态开辟的内存,达到销毁栈空间的目的。
2.4入栈的功能
//入栈,相当于往数组填充数据x
void StackPush(ST* ps,StackDataType x)
{
assert(ps);
if (ps->top == ps->capacity)//申请的动态空间满了,数组的元素和容量相等,需要增容
{
//用relloc函数申请动态空间,创建临时指针变量是怕动态开辟空间失败,增容一般是上一次的2倍
//注意realloc和malloc函数里的参数,realloc有两个,malloc只有一个
StackDataType* tmp = (StackDataType*)realloc(ps->a, sizeof(StackDataType) * ps->capacity * 2);
if (tmp == NULL)//判断是否成功开辟动态空间
{
perror("the mistake is");
}
else //增容成功
{
//printf("增容成功\n");
ps->a = tmp;//将新开辟的动态空间地址赋给指针a
ps->capacity *= 2;
}
}
//不需要增容或增容之后
ps->a[ps->top] = x;//想数组填充数据
ps->top++; /