文章目录
引言
数据结构世界——栈(Stack)
一、栈的概念
栈,是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈
:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈
:栈的删除操作叫做出栈。出数据也在栈顶。
二、栈的模拟实现
栈的实现一般可以使用数组
或者链表
实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。
2.1 定义
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
- 当前元素个数用栈顶top来表示,是栈独有的表示方法
2.2 初始化
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->top = 0;//top指向栈顶元素的下一个位置
pst->capacity = 0;
}
- 栈的初始化,top可以为0或-1,top为0,则指向栈顶元素的下一个位置,top为-1,则指向栈顶元素的位置
- 这里选用top为0,因为后面写的适合方便理解
2.3 销毁
void STDestroy(ST* pst)
{
assert(pst);
free(pst->a);
pst->top = pst->capacity = 0;
}
- 栈的销毁和顺序表一样,直接释放数组空间,将top和capacity置为0
2.4 压栈
void STPush(ST* pst, STDataType x)
{
assert(pst);
if (pst->top == pst->capacity)
{
int newCapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
STDataType* tmp = (STDataType*)realloc(pst->a, newCapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
return;
}
pst->a = tmp;
pst->capacity = newCapacity;
}
pst->a[pst->top++] = x;
}
- 先判断是否需要扩容,因为这里只有压栈函数需要判断扩容,所以就不用专门再写一个扩容函数
- 初始容量为4,后续每次进行2倍扩容(这样扩容比较合理,不会太频繁,也不会太浪费)
- 再将元素压栈,top++
ps:realloc
函数在pst为NULL
时,充当malloc
的作用,所以既可以为初始栈开辟空间,又可以扩容 。
2.5 判空
bool STEmpty(ST* pst)
{
assert(pst);
return pst->top == 0;
}
- 如果top为0,则栈为空,返回真;反之,返回假
2.6 出栈
void STPop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
pst->top--;
}
- 先
assert
断言判断,栈是否为空,因为top不能减到负数 - 再将top- -,掩盖栈顶元素
2.7 获取栈顶元素
STDataType STTop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
return pst->a[pst->top - 1];
}
- 先
assert
断言判断,栈是否为空,因为top不能减到负数 - 再返回栈顶元素,因为这里top指向的是栈顶元素的下一位,所以下标访问时top要减1
2.8 获取栈的元素个数
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
- top指向的是栈顶元素的下一位,所以数值上就是栈的元素个数
这里很多函数实现都很简单,有些操作直接外部对结构体都可以直接实现,但最后还是写成函数封装,因为top的含义有多重,防止别人使用时误解,导致使用错误 。
2.9 元素访问
栈中元素访问(打印),不是用函数实现。因为它的特殊结构,决定了它的元素不能从任意位置访问 ,必须符合后进先出原则才可以。
void TestStack1()
{
ST st;
//初始化
STInit(&st);
//压栈
STPush(&st, 1);
STPush(&st, 2);
printf("%d\n", STTop(&st));
STPop(&st);
STPush(&st, 3);
STPush(&st, 4);
STPush(&st, 5);
//打印
while (!STEmpty(&st))
{
printf("%d\n", STTop(&st));
STPop(&st);
}
//销毁
STDestroy(&st);
}
- 通常用循环的方式进行访问,同时每访问一个元素,就将它弹出栈,在进行下一个元素的访问
三、栈的应用场景
-
函数调用与返回
在程序执行过程中,函数调用和返回是栈结构的一个典型应用。当函数被调用时,其返回地址、局部变量等信息被压入调用栈中;当函数返回时,这些信息从调用栈中弹出并恢复程序的执行状态。 -
浏览器的前进后退
在浏览器中浏览网页时,我们使用的前进和后退功能也是栈结构的一个应用。浏览器将用户访问过的网页地址保存在一个栈中,当用户点击后退按钮时,浏览器从栈顶弹出最近访问的网页地址并跳转到该网页;当用户点击前进按钮时,浏览器将之前弹出的网页地址重新压入栈中并跳转到该网页。 -
表达式求值
在编译器和解释器中,表达式求值也是栈结构的一个重要应用。编译器和解释器将表达式转换为逆波兰表示法(Reverse Polish Notation, RPN),并使用栈来存储和操作操作数和操作符,从而实现表达式的求值。
标签:1.3,top,元素,栈顶,st,assert,线性,数据结构,pst From: https://blog.csdn.net/2301_79188764/article/details/139569692