栈是一种基本但强大的数据结构,它在许多算法和系统功能中扮演着关键角色。在这篇文章中,我们将深入探讨如何在实现一个栈,从基本概念到具体的代码实现,再到实际应用场景的探讨。
在深入代码之前,先简单介绍栈的概念。栈是一个项的有序集合,其中添加(推入)和删除(弹出)项总发生在同一端,称为“栈顶”。他是后进先出的,就好像弹夹里面的子弹一样
数组的优点在于实现简单,访问时间快。但其缺点是大小固定,可能会有空间浪费或不足的问题。
链表的优点在于可以动态调整大小,内存利用率高。但其缺点是相对于数组,访问时间可能会稍慢。
我们用数组来实现
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
STDataType top;
STDataType capacity;
}ST;
接口
void STInit(ST* pst);
void STDestroy(ST* pst);
void STPush(ST* pst,STDataType x);
void STPop(ST* pst);
STDataType STTop(ST* pst);
bool STEmpty(ST* pst);
int STSize(ST* pst);
初始化是栈实现中的第一步。我们需要将栈顶 top 的初始值设为0,以表示栈为空,在这个实现中,栈被定义为包含动态数组、栈顶指针和容量的结构体。初始化函数 STInit 负责设置这些属性的初始状态。
// 初始化栈
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL; // 初始时,数组指针为空
pst->top = 0; // 栈顶指针初始为0,表示栈为空
pst->capacity = 0; // 初始容量为0
}
在实现压栈操作时,我们要考虑栈可能满的情况。如果栈已满,我们应该阻止进一步的压栈并给出适当的反馈。
// 检查并扩展栈的容量
void SLCheckCapacity(ST* pst)
{
assert(pst);
if (pst->top == pst->capacity)
{
int newCapacity = (pst->capacity == 0) ? 4 : pst->capacity * 2;
STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newCapacity);
if (tmp == NULL)
{
perror("realloc fail");
exit(1); // 如果内存分配失败,则退出程序
}
pst->a = tmp;
pst->capacity = newCapacity;
}
}
// 向栈中推入一个元素
void STPush(ST* pst, STDataType x)
{
assert(pst);
SLCheckCapacity(pst); // 检查并扩展容量
pst->a[pst->top] = x; // 存放元素
pst->top++; // 栈顶指针增加
}
弹栈时,我们需要确保栈不为空。如果尝试从空栈中弹出元素,应该返回一个错误指示。
// 从栈中弹出一个元素
void STPop(ST* pst)
{
assert(pst);
assert(pst->top > 0); // 确保栈不为空
pst->top--; // 栈顶指针减少
}
这些操作相对简单,但它们对于栈的有效使用至关重要。
// 获取栈顶元素
STDataType STTop(ST* pst)
{
assert(pst);
assert(pst->top > 0); // 确保栈不为空
return pst->a[pst->top - 1]; // 返回栈顶元素
}
// 检查栈是否为空
bool STEmpty(ST* pst)
{
assert(pst);
return pst->top == 0; // 如果栈顶指针为0,则栈为空
}
在这个静态数组实现中,destroy 函数不是必须的,但是我们使用了动态分配的内存,则需要在此释放内存。
// 销毁栈
void STDestroy(ST* pst)
{
assert(pst);
free(pst->a); // 释放栈内部的数组空间
pst->a = NULL; // 将数组指针置为空
pst->top = 0; // 栈顶指针重置为0
pst->capacity = 0; // 容量重置为0
}
int main()
{
ST stack; // 定义栈变量
ST* pst = &stack; // pst 指向 stack
STInit(pst); // 初始化栈
// 向栈中添加元素
STPush(pst, 1);
STPush(pst, 2);
STPush(pst, 3);
STPush(pst, 4);
// 打印并弹出栈中的元素
while (!STEmpty(pst))
{
printf("%d ", STTop(pst));
STPop(pst);
}
printf("\n");
STDestroy(pst); // 销毁栈
return 0;
}
运行结果如下:
栈在计算机科学中有着广泛的应用。从函数调用的内存管理到算法中的临时数据存储,栈的应用无处不在。理解栈如何在这些场景中工作,对于充分利用其潜力至关重要。
栈不仅是一种基本的数据结构,而且是理解更复杂系统和算法的基石。通过在c语言中实现和应用栈,我们不仅能够加深对这一结构的理解,还能够提高我们的编程技巧和问题解决能力。我希望这篇文章能够帮助你更好地理解和实现栈。如果你有任何问题或想分享你的经验,请在评论区留言。