栈也是一种线性结构,不过他比较特殊,他的特点是:先进后出,后进先出,大体形式如下图
1. 结构体定义为:
这里我们选用数组来进行模拟栈,链表也可以实现但是实际使用可能并没有数组好用原因如下
数组的特点是可以随机访问,并且高速缓存的命中高,但是他的插入操作时间复杂度高不适合频繁的插入删除工作,但是由于栈的特点是先进后出,后进现出,数组很快能找到最后一个元素,插入删除方便
如果使用链表来实现,我们并不能快速的访问各个元素,并且缓存命中低,所有我们这里选用的是数组来描述
但是数组需要连续的开辟一段空间我们并不知道这个空间的大小是多少,于是这里我们采用动态开辟空间,所以我们需要一个指针,一个top记录栈顶,以及一个capacity记录总的容量
typedef int Elemtype;
typedef struct Stack
{
Elemtype* a;
int top;
int capacity;
}Stack;
2.接口
void StackInit(Stack* sp);//初始化
void StackPush(Stack* sp, Elemtype x);//入栈
void StackPop(Stack* sp);//出栈
Elemtype StackTop(Stack* sp);//获得栈顶的数据
int StackSize(Stack* sp);//当前栈的大小
bool StackEmpty(Stack* sp);//是否为空 是为1 不是为0
void StackDestory(Stack* sp);//销毁栈
3.接口的实现
栈的接口实现起来相对来说比较简单
注意1,top的指定规则,他是指向最后一个数据,还是指向最后一个数据的下一个地址,
在这里我是将top设计成指向下一个数据的地址,即栈顶
注意2,求top的数值和出栈必须是空间内数据不为空才可以 所有我们需要进行判断
void StackInit(Stack* sp)
{
assert(sp);
//第一种初始化 什么都不给嗷
/*sp->a = NULL;
sp->capacity = 0;
sp->top = 0;*/
//第二种初始化,直接先给一些空间
sp->a = (Elemtype*)malloc(sizeof(Elemtype) * 4);
sp->top = 0;
sp->capacity = 4;
}
void StackPush(Stack* sp, Elemtype x)
{
assert(sp);
if (sp->top == sp->capacity)
{
Elemtype* tmp =(Elemtype*)realloc(sp->a, sizeof(Elemtype)*sp->capacity* 2);
if (tmp == NULL)
{
exit(-1);
}
sp->a = tmp;
sp->capacity *= 2;
}
sp->a[sp->top] = x;
sp->top++;
}
void StackPop(Stack* sp)
{
assert(sp);
assert(!StackEmpty(sp));
sp->top--;
}
Elemtype StackTop(Stack* sp)
{
assert(sp);
assert(!StackEmpty(sp));
return sp->a[sp->top - 1];
}
int StackSize(Stack* sp)
{
assert(sp);
return sp->top;
}
bool StackEmpty(Stack* sp)
{
assert(sp);
return sp->top == 0;
}
void StackDestory(Stack* sp)
{
assert(sp);
free(sp->a);
sp->a = NULL;
sp->top = 0;
sp->capacity = 0;
}
4.测试
写好了接口当然要测试一下,正不正确
这里我们先入栈1,2然后出栈,所有打印的先是2,随后入栈3,4,5,根据先进后出,后进先出最后打印结果和预期完全相同
栈的实现完成!