在之前我们已经学习了数据结构中线性表里面的顺序表与链表,了解了如何实现顺序表与链表增、删、查、该等功能。其实在线性表中除了顺序表和链表还有其他的类别,在本篇中我们就将学习另外一种线性表——栈,在通过本篇的学习后,你将会对栈的结构有充足的了解,在了解完结构后我们还将进行栈的实现。一起加油吧!!!
1.栈的概念与结构
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的⼀端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出(先进后出)lifo(last in first out的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
那么栈的底层结构是基于什么的呢?其实基于数组还是基于单链表或者是双链表都是可以的,那么哪一种是最优解呢?接下来我们就来分析基于不同底层结构的利与弊
通过以上的分析可以发现无论栈的底层结构是基于数组还是单链表还是双链表在入栈和出栈时的时间复杂度都为o(1),但是综合其他因素使用数组是最优解
2.栈的实现
在实现栈的代码内在stack.h头文件内定义栈的结构以及对各种功能函数进行声明,在stack.c文件内实现各个函数,在test.c文件内对实现的各函数进行测试
2.1栈结构的定义
在stack.h中创建一个结构体来定义栈的结构,在该结构体中的成员变量和顺序表中基本是相同的,只不过将顺序表中表示有效元素个数的size改名位top,让top来表示栈顶
//定义栈的结构
typedef int stdatatype;
typedef struct stack
{
stdatatype* a;
int capacity;//栈空间大小
int top;//栈顶
}stack;
2.2栈的初始化
要完成栈的初始化函数首先要在stack.h完成初始化函数的声明
//初始化栈
void stackinit(stack* ps);
将该函数命名为stackinit,函数的参数就为指向结构体的指针
接下来就是在stack.c内完成初始化函数的实现
由于ps指针是指向结构体的指针,所以该指针不能为空,所以要对ps进行assert断言
//初始化栈
void stackinit(stack* ps)
{
assert(ps);
ps->a = null;
ps->top = ps->capacity = 0;
}
2.3检测栈是否为空
要完成检测栈是否为空函数首先要在stack.h完成该函数函数的声明
//检测栈是否为空
bool stackempty(stack* ps);
将该函数命名为stackempty,函数的参数就为指向结构体的指针,函数的返回类型为布尔类型
接下来就是在stack.c内完成检测栈是否为空函数的实现
由于ps指针是指向结构体的指针,所以该指针不能为空,所以要对ps进行assert断言
在该函数中若数组内的有效个数top为0函数就会返回true,不为0就返回false
//检测栈是否为空
bool stackempty(stack* ps)
{
assert(ps);
return ps->top==0;
}
2.4入栈
要完成入栈函数首先要在stack.h完成初始化函数的声明
void stackpush(stack* ps,stdatatype x);
将该函数命名为stackpush,函数的参数有两个,第一个为指向结构体的指针,第二个为要进入栈的数据
接下来就是在stack.c内完成入栈函数的实现
由于在入栈时数组的空间可以已经满了,因此在进行插入数据之前要判断数组的有效个数是否与数组的空间大小相同,如果相同就需要进行增容。增容的代码就和之前的顺序表检测数组空间大小函数一样。
在调整完空间大小之后的入栈就和顺序表中得尾插一样,ps->a[ps->top++] = x一句代码就可以实现
//入栈
void stackpush(stack* ps,stdatatype x)
{
assert(ps);
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
stdatatype* tmp = (stdatatype*)realloc(ps->a, newcapacity * sizeof(stdatatype));
if (tmp == null)
{
perror("realloc fail!");
exit(1);
}
ps->a=tmp;
ps->capacity = newcapacity;
}
ps->a[ps->top++] = x;
}
2.5出栈
要完成出栈函数首先要在stack.h完成出栈函数的声明
//出栈
void stackpop(stack* ps);
将该函数命名为stackpop,函数的参数为指向结构体的指针
接下来就是在stack.c内完成出栈函数的实现
由于ps指针是指向结构体的指针,所以该指针不能为空,所以要对ps进行assert断言
同时在出栈时栈不能为空,所以要对satckempt(ps)进行assert断言
在出栈就和顺序表中得尾删一样将top减一就可
//出栈
void stackpop(stack* ps)
{
assert(ps);
assert(!stackempty(ps));
--ps->top;
}
2.6取栈顶元素
要完成取栈顶元素函数首先要在stack.h完成取栈顶元素函数的声明
//获取栈顶元素
stdatatype stacktop(stack* ps);
将该函数命名为stacktop,函数的参数为指向结构体的指针,函数的返回值就为栈顶的元素
接下来就是在stack.c内完成取栈顶原始函数的实现
由于ps指针是指向结构体的指针,所以该指针不能为空,所以要对ps进行assert断言
同时在出栈时栈不能为空,所以要对satckempt(ps)进行assert断言
由于top指向数组的尾元素的后一位,所以只要将size-1就可以得到栈顶元素
//获取栈顶元素
stdatatype stacktop(stack* ps)
{
assert(ps);
assert(!stackempty(ps));
return ps->a[ps->top - 1];
}
2.7获取栈中有效元素个数
要完成获取栈中有效元素个数函数首先要在stack.h完成获取栈中有效元素个数函数的声明
//获取栈中有效元素个数
int stacksize(stack* ps);
将该函数命名为stacksize,函数的参数为指向结构体的指针,函数的返回值就为栈的元素个数
接下来就是在stack.c内完成获取栈中有效元素个数函数的实现
由于ps指针是指向结构体的指针,所以该指针不能为空,所以要对ps进行assert断言
因为在结构体中的top就为数组的有效元素个数,所以在该函数中返回ps->top即可
//获取栈中有效元素个数
int stacksize(stack* ps)
{
assert(ps);
return ps->top;
}
2.8销毁栈
要完成销毁栈函数首先要在stack.h完成销毁栈函数的声明
//销毁栈
void stackdestory(stack* ps);
将该函数命名为stackdestory,函数的参数为指向结构体的指针
接下来就是在stack.c内完成销毁栈函数的实现
由于ps指针是指向结构体的指针,所以该指针不能为空,所以要对ps进行assert断言
在销毁时,由于栈的空间是由realloc申请来的,所以在使用完后要用free来释放内存空间,并且在释放后将指针ps->a置为null,且将top和capacity都赋值为0
//销毁栈
void stackdestory(stack* ps)
{
assert(ps);
if (ps->a)
{
free(ps->a);
}
ps->a = null;
ps->top = ps->capacity = 0;
}
2.9栈的打印
因为在栈和顺序表以及链表不同,由于栈只能在一端进和出所以栈是不能遍历,所以我们不能通过创建一个函数来遍历栈
所以要打印栈就只能在test.c内调用相关函数来实现
例如以下栈先依次入栈1,2,3,4,之后要打印就通过以下循环来实现
#include"stack.h"
void test()
{
stack ps;
stackinit(&ps);
stackpush(&ps, 1);
stackpush(&ps, 2);
stackpush(&ps, 3);
stackpush(&ps, 4);
while (!stackempty(&ps))
{
stdatatype p = stacktop(&ps);
printf("%d ", p);
stackpop(&ps);
}
stackdestory(&ps);
}
int main()
{
test();
return 0;
}
3.栈的实现完整代码
stack.h
#define _crt_secure_no_warnings 1
#include<stdio.h>
#include<stdbool.h>
#include<assert.h>
#include<stdlib.h>
//定义栈的结构
typedef int stdatatype;
typedef struct stack
{
stdatatype* a;
int capacity;//栈空间大小
int top;//栈顶
}stack;
//初始化栈
void stackinit(stack* ps);
//销毁栈
void stackdestory(stack* ps);
//检测栈是否为空
bool stackempty(stack* ps);
//入栈
void stackpush(stack* ps,stdatatype x);
//出栈
void stackpop(stack* ps);
//获取栈顶元素
stdatatype stacktop(stack* ps);
//获取栈中有效元素个数
int stacksize(stack* ps);
stack.c
#include"stack.h"
//初始化栈
void stackinit(stack* ps)
{
assert(ps);
ps->a = null;
ps->top = ps->capacity = 0;
}
//销毁栈
void stackdestory(stack* ps)
{
assert(ps);
if (ps->a)
{
free(ps->a);
}
ps->a = null;
ps->top = ps->capacity = 0;
}
//检测栈是否为空
bool stackempty(stack* ps)
{
assert(ps);
return ps->top==0;
}
//入栈
void stackpush(stack* ps,stdatatype x)
{
assert(ps);
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
stdatatype* tmp = (stdatatype*)realloc(ps->a, newcapacity * sizeof(stdatatype));
if (tmp == null)
{
perror("realloc fail!");
exit(1);
}
ps->a=tmp;
ps->capacity = newcapacity;
}
ps->a[ps->top++] = x;
}
//出栈
void stackpop(stack* ps)
{
assert(ps);
assert(!stackempty(ps));
--ps->top;
}
//获取栈顶元素
stdatatype stacktop(stack* ps)
{
assert(ps);
assert(!stackempty(ps));
return ps->a[ps->top - 1];
}
//获取栈中有效元素个数
int stacksize(stack* ps)
{
assert(ps);
return ps->top;
}
发表评论