栈是什么
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。
- 进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。
- 栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
- 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
- 出栈:栈的删除操作叫做出栈。出数据也在栈顶。
栈也是一种线性表,是一种特殊的数据结构,我们可以用我们前面所学的知识来实现栈
我们之前学习的顺序表和链表只是单纯的存储数据,并可以做到增删查改的作用,而栈除了存储数据还要达到后进先出的性质
- 如图我们假设,数据入栈完成后再出,即先存储1 2 3 4后再出数据,按照后进先出原则,我们的出栈顺序就是4 3 2 1
- 倘若不要求入栈完成再出,就有很多种,如1 2 3 4,即入一个出一个。3 2 4 1即入把3 2出了后再入
- 但没有3 1 2 4这样的顺序,2没有可能比1先出。
助力理解:
1.一个栈的初始状态为空。现将元素1、2、3、4、5、A、B、C、D、E依次入栈,然后再依次出栈,则元素出
栈的顺序是( B)。
A 12345ABCDE
B EDCBA54321
C ABCDE12345
D 54321EDCBA
2.若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是(C)
A 1,4,3,2
B 2,3,4,1
C 3,1,4,2
D 3,4,2,1
接下来就是实现栈了,有2种方式实现
- 数组栈
- 链式栈
这里我们用数组来实现栈会更方便一点,数组的高数缓存命中更高,并且结构很符合栈的需求,尾部就可以当栈顶,尾插尾删即可

如果选链表实现的话,我们的的尾删就要遍历链表找到前一个太慢,只能将头当成栈顶,进行头删头插
定义结构体
typedef int DataType;
typedef struct stack
{
DataType* p;
int top;
int num;
}st;
- 我们在写较长代码的时候,我们为了方便可以将数据类型typedef一下,比如这里的int起别名叫DataType,当我们下次针对char类型的时候,一改即可。
- 栈的实现需要一个指针,指向我们开辟的数组的起始位置。
- top表示栈顶位置,可以理解为开辟空间里面的有效个数。
- num表示现在空间的大小。
初始化
void stinit(st* pst)
{
assert(pst);
pst->p = NULL;
pst->top = 0; //这里top也可以赋值-1
pst->num = 0;
}
这是我们的一种初始化方式,即先不开辟空间,这里我们重点讲top的赋值
- 当 top = 0 的时候

我们的top是指向数据的下一个位置,因为我们是先插入数据,再让top++

如图,top先指向0,插入数据后top++,即top指向的是栈顶元素的下一个位置
-
当 top = -1 的时候
与上面相似,不够我们是让top++,再插入数据,这个时候top就指向栈顶元素
我们一定要分清楚自己想让top指向栈顶元素还是栈顶元素的下一个,不然你想指向栈顶元素,却赋值为0,那这就说明这个时候栈里面就已经有数据呢,但你还没插入。
要重视top的赋值,不然后面的所以操作都会收到影响,这里使用 top = 0 的情况
销毁空间
void stdestroy(st* pst)
{
assert(pst);
free(pst->p);
pst->p == NULL;
pst->num = 0;
pst->top = 0;
}
值得注意的是,这里的free是free全部开辟的空间,数组空间连续,并不是你的链表一个一个释放
入栈
我们的栈没有头和尾的概念,所以我们的命名就是stpush
void stpush(st* pst, DataType x)
{
assert(pst);
if (pst->top == pst->num)
{
int newnum = pst->top == 0 ? 4 : pst->num * 2;
DataType* newp = (DataType*)realloc(pst->p, sizeof(DataType) * newnum);
if (!newp)
{
perror("realloc");
return;
}
pst->p = newp;
pst->num = newnum;
}
pst->p[pst->top] = x;
pst->top++;
}
值得注意的是,我们这里的初始化不开辟空间和这里呼应上了
int newnum = pst->top == 0 ? 4 : pst->num * 2;
DataType* newp = (DataType*)realloc(pst->p, sizeof(DataType) * newnum);
我们的newp是赋值给pst->p的不要把类型写出st*了
我们的
这是一种写法,推荐学习
这里的memblock指针为NULL的时候,realloc表现如同malloc一样
我们并不需要单独写一个函数去扩容,因为复用的函数很少,这个push函数把扩容也包括了
出栈
我们只需要让top–即可
void stpop(st* pst)
{
assert(pst);
assert(!stempty(pst));
pst->top--;
}
注意,为了代码的可读性,我们推荐写上一个判空的函数STEmpty
获取栈顶元素
我们为什么不在主函数中直接用下标访问呢?
如: void test() { st st; st.p[st.top]; } 因为这样对使用者的需求过大,使用者不知道你的top是指向哪的,当top为0的时候,使用者可能会当成下标来访问了,打印出随机值来,当top为-1的时候,使用者会感到疑惑
我们用函数来实现获取栈顶元素
DataType sttop(st* pst)
{
assert(pst);
return pst->p[pst->top - 1];
}
判空函数
bool stempty(st* pst)
{
assert(pst);
/*if (!pst->top)
{
return true;
}
else
{
return false;
}*/
return pst->top == 0;
}
我们要学习一下,这里是一个比较操作符,等于0就是真,反之为假
获取栈中元素个数
DataType stsize(st* pst)
{
assert(pst);
return pst->top;
}
打印元素
- 我们不能自己创造一个print函数来打印栈中的元素,因为栈的后进先出的性质,我们就不能随机访问里面的元元素了
- 我们只能出一个访问一个
while (!stempty(&st))
{
printf("%d ", sttop(&st));
stpop(&st);
}
真,反之为假
获取栈中元素个数
DataType stsize(st* pst)
{
assert(pst);
return pst->top;
}
打印元素
- 我们不能自己创造一个print函数来打印栈中的元素,因为栈的后进先出的性质,我们就不能随机访问里面的元元素了
- 我们只能出一个访问一个
while (!stempty(&st))
{
printf("%d ", sttop(&st));
stpop(&st);
}
![**[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xaThLV3d-1684251023742)(C:\Users\李诗琦\AppData\Roaming\Typora\typora-user-images\image-20230515161041163.png)]**](/https://i-blog.csdnimg.cn/blog_migrate/e6dc344eac94515037e03e037c188d85.png)



1万+

被折叠的 条评论
为什么被折叠?



