目录
一 .概念以及结构
栈是顺序表和链表的连续,一种类似的结构,但也有独特的性质.
栈是一种特殊的线性表,在顺序表和链表,可以在任意位置进行插入删除.而栈只允许在固定的一端进行插入删除.对进行插入删除的一端称为:栈顶,另一端称为:栈底。栈中的数据遵循后进先出的原则,只能在栈顶进行出数据和入数据,插入叫压栈,删除叫出栈。
可以把栈想象成一个弹夹,先压进去的子弹后打出去,后后压进去的子弹先打出去.
如何实现栈,有两种方式来实现栈,分别为:数组栈、链式栈
对于用数组,栈底等于头,也可以说下标为0,栈顶等于尾
对于用双向链表,栈顶可以是尾节点,也可以是头节点。用单链表就不一样了,如果继续用尾节点作为栈顶,那么就导致尾插很方便,而尾删就十分麻烦了,所以栈顶只能是头节点
综合考虑来看,用数组来实现栈是比较方便的,对于需要扩容这方面其实影响不大,需要很久才会扩容一次影响很小。而且对于计算机来说,读取一次数组栈,就能读取到后续的数据。如果要从链表中选一个,最好的还是单链表。
所以接下来选用数组栈进行实现
二 . 功能实现
2.1基本数据管理
实现数组栈也是多个结构,需要用到结构体来管理。
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
STDataType top; //标识栈顶位置的
STDataType capacity;
}ST;
这里定义的top就是用来表示栈顶位置,capacity就是用来扩容。
2.2基本功能
2.2.1初始化
首先进行断言,确保结构体不为空.接着可以选择将a置为空,容量为0.方便之后的扩容.最后才是栈最重要的点,关于栈顶top是什么?可能会想当然的将top设置为0.那么来看看会发生什么.
因为要把栈置为空,所以将top为0.当插入了一个数据后,top为了继续指向栈顶,top依旧为0.这就出现了冲突.当看到top等于0的时候,就无法判断是一个元素还是空
所以结合上述问题,当栈为空时置为0,有元素插入时,变为1.
也可以当栈为空时top设置为-1,当有元素插入后,变为0.top == 1指向栈顶元素的下一个位置
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->capacity = 0;
pst->top = 0;
}
2.2.2销毁
void STDestroy(ST* pst)
{
assert(pst);
free(pst->a); //释放
pst->a = NULL; //置空
pst->top = pst->capacity = 0;
}
2.2.3入栈
对于初始化提到的两种解决栈顶的方法,对应到插入后写法需要改变.
当top == -1时,需要对top先自增后赋值.
当top == 0时,需要先赋值再自增
//top 为 0时
void STPush(ST* pst , STDataType x)
{
assert(pst);
if (pst->top == pst->capacity)
{
int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
STDataType* tmp = realloc(pst->a, sizeof(STDataType) * newcapacity);
if (tmp == NULL)
{
perror("realloc fail");
return;
}
pst->a = tmp;
pst->capacity = newcapacity;
}
pst->a[pst->top] = x;
pst->top++;
}
当top == -1时,只需要将最后两行代码交换位置即可.
2.2.4出栈
出栈就很简单了,只需要将top自减即可,但也要保证top不为0.
void STPop(ST* pst)
{
assert(pst);
assert(pst->top>0);
pst->top--;
}
2.2.5获取栈顶元素
只需要设定返回值为top-1,为栈顶元素的下一个.
STDataType STTop(ST* pst)
{
assert(pst);
assert(pst->top > 0);
return pst->a[pst->top - 1];
}
2.2.6获取栈中有效元素个数
同样,当top指向栈顶元素,那么等于-1就为空;当top指向栈顶元素的下一个位置,那么等于0就为空
bool STEmpty(ST* pst)
{
assert(pst);
if (pst->top == 0)
{
return true;
}
else
{
return false;
}
}
也有更简洁的写法,直接返回判断值即可,不需要写if判断.
return pst->top == 0;
2.2.7检测栈是否为空
同理,当top指向栈顶元素,那么等于+1就是个数;当top指向栈顶元素的下一个位置,那么top等于个数
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
2.2输出栈元素
栈与顺序表,单链表不同,需要严格按照规定来进行打印,通过循环判断栈是否为空来进行打印,打印后再出栈一个,继续打印.之所以这样是必须遵循栈的先进后出
while (!STEmpty(&s))
{
printf("%d ", STTop(&s)); //获取栈顶并打印
STPop(&s); //移除栈顶元素,打印下一个
}
printf("/n");
那么思考一下,当以12345入栈后,出栈一定是54321?答案是 不一定.来实验一下:
可以看到,其实并不是按照之前所想的那样,因为当5和4进栈的时候,3已经出栈了,不能和3相比,只能跟在栈里的数据相比,所以栈的后进先出是相对的.所以入栈顺序和出栈顺序是一对多的关系,入栈顺序只有一种;而出栈顺序则有很多种.但出栈顺序也不是任何种类都行
若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是()
(A) 1,4,3,2 (B) 2,3,4,1 (C) 3,1,4,2 (D) 3,4,2,1
做这个题只需要动手画一下顺序,推导一下即可得到答案
A: 入1出1,剩下的依次入栈
B: 入1,入2出2,入3出3,入4出4.
C:无法实现
D:入12,再入34出34
所以答案选C.
栈的知识点就到这里,后续会有关于应用栈的OJ题目