目录
1.栈的概念和结构
栈是一种特殊的线性表,其只允许在固定的一端进行删除和插入操作,进行插入和删除的一端称为栈顶,另一端成为栈底。栈的特殊就在于它是一种先进后出的结构
压栈:栈插入数据的操作叫做压栈/进栈/入栈,入数据在栈顶。
出栈:栈的删除数据操作叫做出栈,出数据也在栈顶。
如图

咱们来思考栈的底层用的是链表还是数组?栈是需要频繁的进行插入数据和删除数据的,而如果使用链表作为栈的底层逻辑的话,我们需要每次都遍历到含有NULL的结点,这样的时间复杂度会比较高,而我们要是使用数组来实现栈的话,只需要在后面直接插入数据即可。
2.栈的实现
(1)栈的初始化
typedef int Stackdatatype;
typedef struct Stack
{
Stackdatatype * arr;
int top;
int capacity;
}Stack;
栈可以存储任何类型的数据,所以我们使用关键字typedef重命名,选择top来表示栈顶的元素,capcity来表示栈的容量。
接下来,我们来试着给栈初始化
typedef int Stackdatatype;
typedef struct Stack
{
Stackdatatype * arr;
int top;
int capacity;
}Stack;
void Stackinit(Stack * ps)//使用ps指针变量来接收st的地址
{
ps->arr = NULL;
ps->top = ps->capacity = 0;
}
int main()
{
Stack st;//先定义结构体变量st
Stackinit(&st);//把结构体变量st的地址传过去
}
(2)栈的销毁
栈的销毁可以说与栈的初始化如出一辙,把指向栈的指针arr置为NULL,并把top以及capacity置为0即可。
(3)入栈
入栈是从栈顶放进数据的,先放的数据落入栈顶,但是我们需要对栈进行扩容,也就是增加capacity。
typedef int Stackdatatype;
typedef struct Stack
{
Stackdatatype * arr;
int top;
int capacity;
}Stack;
void Stackinit(Stack * ps)//使用ps指针变量来接收st的地址
{
ps->arr = NULL;
ps->top = ps->capacity = 0;
}
void Stackcheck(Stack * ps)
{
int newcapacity = ps->capacity == 0? 4 : 2*capacity;//为0,newcapacity就为4,不为0就是capacity的两倍,相当于扩容两倍
Stack * tmp = (Stack *)realloc(ps->arr,sizeof(Stackdatatype)*newcapacity);
if(tmp == NULL)
{
return NULL;//如果扩容失败,就返回空指针NULL
}
//走到这里,说明扩容成功
ps->arr = tmp;//把扩容成功后指向该空间的指针重新赋值给arr
capacity = newcapacity;
}
int main()
{
Stack st;//先定义结构体变量st
Stackinit(&st);//把结构体变量st的地址传过去
Stackcheck(&st);
}
现在,我们有四个int型内存的空间了,如图

那么,我们现在入栈就是从栈顶放入数据,最终会落入栈底,假设我们现在入栈一个4
void Stackcheck(Stack * ps)
{
int newcapacity = ps->capacity == 0? 4 : 2*capacity;//为0,newcapacity就为4,不为0就是capacity的两倍,相当于扩容两倍
Stack * tmp = (Stack *)realloc(ps->arr,sizeof(Stackdatatype)*newcapacity);
if(tmp == NULL)
{
return NULL;//如果扩容失败,就返回空指针NULL
}
//走到这里,说明扩容成功
ps->arr = tmp;//把扩容成功后指向该空间的指针重新赋值给arr
capacity = newcapacity;
void Stackpush(Stack * ps,x)//用x来接收4
{
if(ps->top == ps->capacity)//top跟capacity相等就说明栈的空间满了,需要扩容
{
Stackcheak(ps);
}
ps->arr[ps->top] = x;
ps->top++;
}
int main()
{
Stack st;
Stackpush(&st,4);//在st这个栈中,入一个4
return 0;
}
入栈成功,如下图

现在我们想要再入几个数据,例如我们想要入3 ,2 ,1
即下图
(4)出栈
入上图,出栈就很直观了。我们先入的4,只能放在最底下等着最后一个出栈;我们最后入的栈1,就可以最先出栈。也就印证了栈的特点——先入后出
现在,我们来实现出栈的操作,其实出栈很简单,直接top--,把1给覆盖了,如图

但是,我们需要注意一个前提,就是栈为空的时候(栈中不存放任何数据),是不能出栈的。你栈中都没数据,你出栈干嘛呢?你说是吧
所以我们要提前判断栈是否为空。上代码
bool Stackempty(Stack * ps)
{
assert(ps);//ps不能为空,不然这个判断就无意义了
return ps->top == 0;//如果top为0,就返回true,反之,返回false
}
void Stackpop(Stack * ps)//出栈的函数
{
assert(!Stackempty(ps));//栈不为空,才能进行出栈操作
ps->top--;//top往下走一步,覆盖掉最前面的数据
}
(5)取栈顶元素
请注意,这里是取栈顶元素,并不是删除栈顶的元素,只是返回栈顶的元素,并不影响原先的栈结构
假设我们入栈4 3 2 1,那么栈顶就是1,我们就把1返回
代码显示如下
Stackdatatype StackTop(Stack * ps)
{
return ps->arr[ps->top - 1];
}
(6)获取栈中有效元素个数
这个就很简单了,只需要把栈中的元素个数返回就好了,假设我们栈中存储了5个数据,那就返回数字5
代码示例:
int Stacksize(Stack * ps)
{
return ps->top;//top的大小刚好就为栈中的的元素个数
}
(7)栈是否为空
前面已经讲过了,其实就调用布尔类型的函数即可
本篇博客就介绍到这里,有疑问的可以私信博主,博主免费为大家解答
520






