目录
一 了解栈
- 概念: 栈(Stack)是一种遵循先入后出(LIFO)的逻辑的线性数据结构。你可以把它的结构看做一个弹夹,子弹从弹夹上方压入弹夹(入栈/压栈),也只能从弹夹上方取出来(出栈)。由于栈的特点,所以限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。因此栈可以视为一种受限制的数组或链表。
- 应用: 每次调用函数时,系统都会在栈顶添加一个栈帧,用于记录函数的上下文信息。在递归函数中,向下递推阶段会不断执行入栈操作,而向上回溯阶段则会不断执行出栈操作。等
二 栈的实现
(1)定义链栈/顺序栈; (2)栈的初始化/构造;
扫描二维码关注公众号,回复: 17526268 查看本文章![]()
(3)获取栈的长度; (4)判断栈是否为空;
(5)获取栈顶元素; (6)入栈;
(7)出栈; (8)销毁栈
1, 栈的链式存储结构
若不是很了解链式存储结构,可参考这篇文章:链表入门:“单链表“的基本操作详解(C语言)
https://blog.csdn.net/Mzyh_c/article/details/133841591?spm=1001.2014.3001.5502
(1)定义链栈
typedef struct Stack
{
//存储节点数据
int data;
struct Stack* next;
}LinkStack;
在main函数中成创建一个 top 指针变量,top指针变量在本代码中直接代表 栈顶结点。//初始化链栈 LinkStack* top = InitStack();
(2)栈的初始化/构造
LinkStack* InitStack()
{
LinkStack* top = (LinkStack*)malloc(sizeof(LinkStack));
//指针top直接指向栈顶结点
top = NULL;
//返回指针top
return top;
}
(3)获取栈的长度
int GetLength(LinkStack** top)
{
//设立一个临时指针用于计算
LinkStack* temp = *top;
int count = 0;
while (temp)
{
//从栈顶开始循环
//当temp不为空指针时则count自增
(temp) = temp->next;
count++;
}
return count;
}
(4)判断栈是否为空
int IsEmpty(LinkStack** top)
{
//当栈顶节点为空时则为空栈
if (*top == NULL)
return 0;//栈为空
else
return 1;//栈不为空
}
(5)获取栈顶元素
ElemType GetTop(LinkStack** top)
{
//由于*top指针指向栈顶节点,所以可以直接访问栈顶元素
return (*top)->data;
}
(6)入栈
void Push(LinkStack** top, int e)
{
LinkStack* p = (LinkStack*)malloc(sizeof(LinkStack));
if (!p)
return;//创建失败则返回
p->data = e;
p->next = *top;
*top = p;
}
注意:虽然链栈是基于链表的,但是当你使用本代码的写法时请不要用 top->next 去链接一个新的结点。这是一种错误的写法,如果这样做的话就会在出栈时无法找到前面的结点。
解析:出栈(push)函数中接收了结构体变量top的地址和一个int类型的变量e,首先创建了新结点 p 来存储要添加的数据,然后将变量 e 赋值给p的数据域,将栈的头结点(栈顶)赋值给 p 的next指针,此时 p->next 这个指针指向了原先的栈顶,最后再用新结点取代栈顶的头结点。(见下图)
![]()
链栈入栈的过程(动图)
(7)出栈
void Pop(LinkStack** top, ElemType* e)
{
if (top == NULL)
return;//空栈则返回
//栈不为空时
*e = (*top)->data;//带回数据
//创建临时指针指向栈顶
LinkStack* temp = *top;
//栈顶变为后继的结点
*top = (*top)->next;
//释放临时指针指向的空间
free(temp);
//给临时指针置空,防止产生野指针
temp = NULL;
}
(8)销毁链栈
void Destroy(LinkStack** top)
{
if (!IsEmpty(top))
return;
int len = GetLength(top);
LinkStack* temp = NULL;
while (len--)
{
temp = *top;
(*top) = (*top)->next;
free(temp); //释放空间
temp = NULL; //防止产生野指针
}
return;
}
2, 栈的顺序存储结构
因为栈只会在一端进行插入和删除操作,用数组效率会比较高,数组在尾上插入数据的代价比较小。触发扩容的话会导致效率降低,但由于扩容是低频操作,因此平均效率更高。
(1)基于数组实现的栈
typedef struct SqStack
{
int* data; //存放数据(根据指针的偏移量,可以当数组用)
int top; //栈顶
int capacity; //栈的容量(使用大容量,防止扩容)
}SqStack;
(2)顺序栈的基本操作实现
/* 初始化顺序栈 */
void InitSqStack(SqStack* s)
{
//给顺序栈开辟空间,base指向开辟好的空间的地址
s->data = (ElemType*)malloc(sizeof(ElemType) * MAXSIZE);
if (s->data == NULL)
return;//开辟空间失败
s->capacity = MAXSIZE;
s->top = -1;//顺序栈为空时
}
/* 判断栈是否为空 */
int IsEmpty(SqStack* s)
{
if (s->top == -1)
return 1;//顺序表为空
else
return 0;//顺序表不为空
}
/* 获取栈顶元素 */
int GetTop(SqStack* s)
{
return s->data[s->top];
}
/* 入栈 */
void Push(SqStack* s, int e)
{
if (s->top == MAXSIZE)
{
return;//顺序栈满了则返回
}
else if (IsEmpty(s))
{
s->top = 0;//空栈则栈顶指向0索引
}
else
{
(s->top)++;//非空非满
}
//对栈顶数据进行赋值
s->data[s->top] = e;
}
/* 出栈 */
int Pop(SqStack* s)
{
if (IsEmpty(s))
{
return;//空栈则返回
}
//获取栈顶元素进行返回
int e = s->data[s->top];
(s->top)--;
return e;
}
/*获取顺序栈的长度*/
int GetLength(SqStack* s)
{
return s->top + 1;
}
/* 清空顺序栈 */
void CleanStack(SqStack* s)
{
//top指向-1,表示空栈
s->top = -1;
}
/* 销毁顺序栈 */
void DestoryStack(SqStack* s)
{
free(s->data);
s->data = NULL;
//空间释放后,s->data还记录着一块没有对应空间的地址,
//为了防止野指针,将s->data置为空指针
s->capacity = 0;
s->top = -1;
}
![]()
顺序栈进栈过程
三 测试效果


队列:栈队和顺序的实现
https://blog.csdn.net/Mzyh_c/article/details/135187848?spm=1001.2014.3001.5501