顺序结构实现栈
- 1. 栈
- 1.1 栈的概念及结构
- 1.2栈的实现
- 2. 栈的各种函数实现
- 3. 全部代码实现
1. 栈
1.1 栈的概念及结构
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
如图所示:
入栈的时候只能从最上方入数据,取的时候也只能从最上方拿数据
1.2栈的实现
栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。
数组实现栈
链表实现栈
如果用单链表实现栈最好用头部做栈顶,因为头插,头删比较方便
2. 栈的各种函数实现
首先,我们先定义一个栈的结构
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top; //栈顶
int capacity; //容量
};
初始化栈
void STInit(ST* st)
{
assert(st);
st->a = NULL;
st->capacity = 0;
//st->top = -1 //top 指向栈顶元素
st->top = 0;//top 指向栈顶元素的下一个
}
这里的top可以初始化成0也可以初始化为-1,但是初始化为0,这里的top就相当于顺序表力的size,那么top始终指向要插入元素的下一个,后续操作的时候也会很方便。
销毁栈
void STDestroy(ST* st)
{
assert(st);
free(st->a);
st->a = NULL;
st->capacity = 0;
st->top = 0;
}
因为栈的空间是动态开辟的,所以在栈使用结束后,需要把动态开辟的空间释放,避免造成内存泄漏。
入栈
void STPush(ST* st,STDataType x)
{
assert(st);
STCheckcapacity(st);
st->a[st->top] = x;
st->top++;
}
这里也可以不把扩容的代码单独写成一个函数,因为这里只有这一个地方会用到扩容操作,但是还是建议封装成一个函数,一方面可以提高代码的可读性,另一方面平时我们写代码的时候这个操作一般都是封装成一个函数,所以这样写可以在一定程度上避免代码出错。
检查是否需要扩容
void STCheckcapacity(ST* st)
{
assert(st);
if (st->top == st->capacity)
{
int newcapacity = st->capacity == 0 ? 4 : st->capacity * 2;
STDataType* ptr = (STDataType*)realloc(st->a, sizeof(STDataType) * newcapacity);
if (ptr == NULL)
{
perror("realloc fail");
exit(1);
}
st->a = ptr;
st->capacity = newcapacity;
}
}
出栈
void STPop(ST* st)
{
assert(st);
assert(!STEmpty(st));
st->top--;
}
注意:栈为空的时候不能删除
取出栈顶元素
STDataType STTop(ST* st)
{
assert(st);
assert(!STEmpty(st));
return st->a[st->top - 1];
}
注意:栈为空的时候不能取栈顶元素,top因为初始化为0,所以指向要插入元素的下一个,所以top-1就是最后一个元素的位置
检测栈是否为空,如果为空返回非零结果,如果不为空返回0
bool STEmpty(ST* st)
{
assert(st);
return st->top == 0;
}
这里的栈的top就相当于顺序表里面的size即有效元素的个数,所以直接判断top是否为0就可以了,当top等于0的时候为栈为空,返回真。
获取栈中有效元素个数
int STSize(ST* st)
{
assert(st);
return st->top;
}
这里的栈的top就相当于顺序表里面的size即有效元素的个数,所以直接返回top就可以了。
3. 全部代码实现
Stack.h
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top; //栈顶
int capacity; //容量
}ST;
//初始化栈
void STInit(ST* st);
//销毁栈
void STDestroy(ST* st);
//入栈
void STPush(ST* st,STDataType x);
//出栈
void STPop(ST* st);
//取出栈顶元素
STDataType STTop(ST* st);
//检测栈是否为空,如果为空返回非零结果,如果不为空返回0
bool STEmpty(ST* st);
//获取栈中有效元素个数
int STSize(ST* st);
Stack.c
#include "Stack.h"
//初始化栈
void STInit(ST* st)
{
assert(st);
st->a = NULL;
st->capacity = 0;
//st->top = -1 //top 指向栈顶元素
st->top = 0;//top 指向栈顶元素的下一个
}
//销毁栈
void STDestroy(ST* st)
{
assert(st);
free(st->a);
st->a = NULL;
st->capacity = 0;
st->top = 0;
}
void STCheckcapacity(ST* st)
{
assert(st);
if (st->top == st->capacity)
{
int newcapacity = st->capacity == 0 ? 4 : st->capacity * 2;
STDataType* ptr = (STDataType*)realloc(st->a, sizeof(STDataType) * newcapacity);
if (ptr == NULL)
{
perror("realloc fail");
exit(1);
}
st->a = ptr;
st->capacity = newcapacity;
}
}
//入栈
void STPush(ST* st,STDataType x)
{
assert(st);
STCheckcapacity(st);
st->a[st->top] = x;
st->top++;
}
//出栈
void STPop(ST* st)
{
assert(st);
assert(!STEmpty(st));
st->top--;
}
//取出栈顶元素
STDataType STTop(ST* st)
{
assert(st);
assert(!STEmpty(st));
return st->a[st->top - 1];
}
//检测栈是否为空,如果为空返回非零结果,如果不为空返回0
bool STEmpty(ST* st)
{
assert(st);
return st->top == 0;
}
//获取栈中有效元素个数
int STSize(ST* st)
{
assert(st);
return st->top;
}
Test.c
#include "Stack.h"
int main()
{
ST st;
STInit(&st);
STPush(&st, 1);
STPush(&st, 2);
STPush(&st, 3);
STPush(&st, 4);
while (!STEmpty(&st))
{
printf("%d ", STTop(&st));
STPop(&st);
}
printf("\n");
STDestroy(&st);
return 0;
}
因为栈是后进先出的,所以要访问栈里面的元素时,必须要把栈当前元素取出才能访问下一个元素,每可以看到在打印栈里面的内容时,会把栈变成空,那么栈里面的内容就没有了,但时这也是实际当中的需求,所以不会有什么影响。
运行结果如图: