一、栈
1.概念与结构
栈:⼀种特殊的线性表,其只允许在固定的⼀端进⾏插⼊和删除元素操作。进⾏数据插⼊和删除操作的⼀端称为栈顶,另⼀端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
栈的实现⼀般可以使⽤数组或者链表实现,相对⽽⾔数组的结构实现更优⼀些。因为数组在尾上插⼊数据的代价⽐较⼩
二、栈的实现
1. 定义栈的结构
c
// 给 int 类型起别名 StackDataType
// 好处:以后想把栈存 char/double 时,只改这一行就行
typedef int StackDataType;
// 栈的结构体定义
typedef struct Stack
{
StackDataType* Data; // 指向动态开辟的数组(真正存数据的地方)
int top; // 栈顶标记:**指向 下一个要入栈的位置下标**(初始化为 0)
int capacity; // 栈当前最大容量(数组能存多少个元素)
} ST; // ST 是结构体 Stack 的别名,方便使用
StackDataType:给数据类型起别名,方便修改
ST:结构体别名,简化代码
Data:动态数组,存真正的数据
top:栈顶下标,初始 0,控制出入栈
capacity:栈容量,用于扩容与判满
2. 栈可以实现的功能
这些都放在stack.h文件
c
//stack.h
#pragma once // 防止头文件被重复包含
// 包含需要的库
#include <stdio.h> // 输入输出 printf
#include <stdlib.h> // malloc、realloc、free
#include <assert.h> // 断言,检查程序错误
#include <stdbool.h> // C语言布尔类型 true / false
// 栈存储的数据类型(这里是 int,想改类型只改这一行)
typedef int StackDataType;
// 栈结构体(数组实现的动态栈)
typedef struct Stack
{
StackDataType* Data; // 指向动态数组,真正存数据的地方
int top; // 栈顶标记:下一个要插入的位置下标
int capacity; // 栈当前最大容量(满了需要扩容)
} ST; // ST 是结构体别名,写代码更简洁
// ====================== 基础核心接口 ======================
// 初始化栈
void STInit(ST* ps);
// 销毁栈(释放内存)
void STDestroy(ST* ps);
// 入栈(把数据 x 压入栈顶)
void STPush(ST* ps, StackDataType x);
// 出栈(删除栈顶元素)
void STPop(ST* ps);
// 取栈顶元素(不删除)
StackDataType STTop(ST* ps);
// 获取栈中有效元素个数
int STSize(ST* ps);
// 判断栈指针是否为空(安全检查)
bool STEmpty(ST* ps);
// 判断栈是否已满(用于扩容)
bool STFull(ST* ps);
// ====================== 打印功能 ======================
// 打印栈(从栈底 → 栈顶)
void StackPrint_From_Bottom_to_Top(ST* ps);
// 打印栈(从栈顶 → 栈底)
void StackPrint_From_Top_to_Bottom(ST* ps);
// ====================== 高级扩展功能 ======================
// 清空栈数据(不释放空间,top 归零)
void STClear(ST* ps);
// 获取栈总容量
int STCapacity(ST* ps);
// 反转栈内容
void STReverse(ST* ps);
// 查找值 x 是否在栈中,找到返回下标,没找到返回 -1
int STFind(ST* ps, StackDataType x);
// 获取栈中最小值
StackDataType STMin(ST* ps);
// 获取栈中最大值
StackDataType STMax(ST* ps);
// 批量弹出 n 个元素
void STPopN(ST* ps, int n);
// 复制栈:把 src 复制到 dest
void STClone(ST* src, ST* dest);
// 判断两个栈内容是否完全相同
bool STEqual(ST* st1, ST* st2);
三、栈的核心函数
这些函数的定义写在stack.c
1.判断指向栈结构体的指针是否为空
cpp
// 判断栈是否为空
// 参数:ps - 指向栈结构体的指针
// 返回值:指针为NULL返回true,否则返回false
bool STEmpty(ST* ps)
{
// 判断传入的栈结构体指针是否为NULL
if (ps == NULL)
{
// 指针为空,返回true
return true;
}
// 指针不为空,返回false
return false;
}
函数功能:用于校验栈结构体指针的合法性,判断传入的 ST* 类型指针是否为空指针,保障后续栈操作的安全性
实现逻辑:
- 函数接收栈结构体指针 ps 作为参数,通过条件判断检测指针是否为 NULL;若为空指针则返回 true,若非空指针则返回 false,仅执行指针合法性校验,不涉及栈内部元素状态的判断
2.判断栈是否为满
cpp
// 功能:判断当前栈是否已经存满元素
// 参数:ps 指向栈结构体的指针
// 返回值:栈满返回 true,栈未满返回 false
bool STFull(ST* ps)
{
// 判断条件:栈顶下标 top 等于 总容量 capacity
// 因为我们的 top 指向【栈顶元素的下一个位置】
// 所以当 top == capacity 时,说明数组已经没有空位可以存新元素
if (ps->top == ps->capacity)
{
// 条件成立 → 栈满,返回 true
return true;
}
// 条件不成立 → 栈未满,返回 false
return false;
}
函数功能:判断当前动态顺序栈是否已满,为扩容操作提供判定依据
实现逻辑:
- 函数接收合法的栈结构体指针
ps,通过比较栈顶标记top与容量capacity的值,若二者相等,说明有效元素已占满全部存储空间,返回true;否则返回false
3.初始化栈
cpp
// 初始化栈
// 参数:ps - 指向栈结构体的指针
void STInit(ST* ps)
{
// 断言:确保传入的栈指针不为空
assert(!STEmpty(ps));
// 将栈的数据区指针置空
ps->Data = NULL;
// 初始化栈顶下标和容量均为0
ps->top = ps->capacity = 0;
}
函数功能:完成栈结构的初始化,为后续栈操作建立合法初始状态。
实现逻辑:
- 合法性校验:通过
assert(!STEmpty(ps))断言校验传入的栈结构体指针非空,避免空指针访问,保证函数执行安全 - 成员初始化:将栈的动态数据数组指针
Data置为NULL,表示初始未分配内存空间;同时将栈顶指针top和容量capacity均赋值为 0,标识栈处于空栈、无存储空间的初始状态,符合动态栈从 0 开始扩容的设计规范
4. 销毁栈(释放内存)
cpp
// 销毁栈
void STDestroy(ST* ps)
{
// 断言检查:确保传入的栈指针不为空
assert(!STEmpty(ps));
// 如果栈的数据指针不为NULL,说明已申请内存
if (ps->Data != NULL)
{
// 释放栈动态申请的内存
free(ps->Data);
}
// 将数据指针置空,防止野指针
ps->Data = NULL;
// 重置栈顶和容量为0,恢复栈的初始状态
ps->top = ps->capacity = 0;
}
函数功能:安全销毁栈结构体,释放动态分配的内存,将栈恢复至未初始化的空状态,避免内存泄漏
实现逻辑:
首先通过断言校验栈指针合法性,确保非空;接着判断数据数组指针是否有效,若有效则调用free释放堆上的数组内存,防止内存泄漏;随后将数组指针置空,并把栈顶标记top与容量capacity归零,使栈处于无内存、无数据的初始空状态
5. 扩容
cpp
// 扩容:检查栈空间是否已满,若已满则进行动态扩容
void STBuy(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 判断栈是否已满,满了则需要扩容
if (STFull(ps))
{
// 计算新容量:初始容量为0时设为4,否则扩容为原来的2倍
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
// 重新分配内存,扩容至新容量大小
StackDataType* tmp = (StackDataType*)realloc(ps->Data, newcapacity * sizeof(StackDataType));
// 判断内存分配是否成功
if (tmp == NULL)
{
// 打印扩容失败原因
perror("realloc failed");
exit(-1);
}
// 更新栈的数据指针指向新扩容的空间
ps->Data = tmp;
// 更新栈的容量为新容量
ps->capacity = newcapacity;
}
}
函数功能:为动态顺序栈提供按需自动扩容能力,解决栈存储空间不足的问题,保障入栈操作可正常执行
实现逻辑:
首先通过断言校验栈指针合法性;调用STFull判断栈是否已满,仅在容量耗尽时执行扩容;采用惰性扩容策略,初始容量为 0 时分配 4 个元素空间,后续以 2 倍大小扩容;使用realloc重新分配连续内存空间,校验返回指针有效性避免内存分配失败;更新数据数组指针与容量字段,完成扩容流程
6.入栈(把数据 x 压入栈顶)
cpp
// 入栈:向栈中添加一个元素
void STPush(ST* ps, StackDataType x)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 扩容检查:如果栈空间不足,自动申请扩容
STBuy(ps);
// 将新元素放入当前栈顶位置
ps->Data[ps->top] = x;
// 栈顶上移,指向新的待插入位置
++ps->top;
}
函数功能:实现数据入栈操作,将指定元素压入栈顶,是栈的核心写入接口
实现逻辑:
首先通过断言校验栈结构体指针合法性,确保非空;调用STBuy函数完成栈容量检查与动态扩容,保证存储空间充足;将元素x存入数组中top指向的下标位置,完成数据写入;最后将top自增,更新栈顶标记,维持栈的正确状态
7.出栈(删除栈顶元素)
cpp
// 出栈:删除栈顶的一个元素
void STPop(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈不为空,防止对空栈执行出栈操作
assert(ps->top > 0);
// 栈顶下移,逻辑删除栈顶元素
--ps->top;
}
函数功能:实现栈顶元素的出栈操作,是栈的核心删除接口
实现逻辑:
通过双重断言保障操作安全,首先校验栈结构体指针非空,其次确保栈内存在有效元素;仅将栈顶标记 top 执行自减操作,逻辑上移除栈顶元素,不进行物理数据清理
8.取栈顶元素(不删除)
c
// 取栈顶元素:获取栈顶的元素值,不删除元素
StackDataType STTop(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈不为空,防止获取空栈的栈顶元素
assert(ps->top > 0);
// 返回栈顶元素(栈顶下标为top-1)
return ps->Data[ps->top - 1];
}
函数功能:获取并返回当前栈顶的有效元素,不修改栈结构与数据内容
实现逻辑:
通过断言校验栈结构体指针合法且栈非空,保证访问有效;根据栈顶标记 top 指向下一个待插入位置的设计规则,返回数组下标为 top - 1 位置的元素,即为当前栈顶元素
9.获取栈中有效元素个数
c
// 获取栈中有效元素个数:返回栈里当前存储的元素数量
int STSize(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈顶下标合法,大于等于0
assert(ps->top >= 0);
// 返回栈顶值,即为有效元素个数
return ps->top;
}
函数功能:获取并返回栈中当前有效元素的数量,提供栈的大小统计能力
实现逻辑:
通过断言校验栈结构体指针合法,且栈顶标记非负;基于top表示下一个待插入位置的设计,直接返回top值作为有效元素个数,无需遍历计算
10. 打印栈(从栈底 → 栈顶)
c
// 打印栈(从栈底到栈顶)
void StackPrint_From_Bottom_to_Top(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈元素个数合法(top 大于等于 0)
assert(ps->top >= 0);
// 打印栈遍历方向提示信息
printf("From_Bottom_to_Top:");
// 循环遍历栈中所有有效元素,从栈底下标0开始到栈顶下标top-1结束
for (int i = 0; i < ps->top; i++)
{
// 逐个打印栈内的元素,元素之间用空格分隔
printf("%d ", ps->Data[i]);
}
// 打印换行符,使输出格式整洁,结束本次打印
printf("\n");
}
函数功能:以栈底到栈顶的顺序遍历并输出顺序栈中所有有效元素,用于调试与状态可视化
实现逻辑:
通过断言校验栈结构体指针非空且栈顶标记合法;打印提示信息后,从数组下标 0 开始遍历至下标 top-1,依次输出所有有效元素;遍历结束后换行,保证输出格式整洁
11. 打印栈(从栈顶 → 栈底)
c
// 打印栈(从栈顶到栈底)
void StackPrint_From_Top_to_Bottom(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈元素个数合法(top 大于等于 0)
assert(ps->top >= 0);
// 打印栈遍历方向提示信息
printf("From_Top_to_Bottom:");
// 循环遍历栈中所有有效元素,从栈顶下标 top-1 开始到栈底下标 0 结束
for (int i = ps->top - 1; i >= 0; i--)
{
// 逐个打印栈内的元素,元素之间用空格分隔
printf("%d ", ps->Data[i]);
}
// 打印换行符,使输出格式整洁,结束本次打印
printf("\n");
}
函数功能:以栈顶到栈底的顺序遍历并输出顺序栈中所有有效元素,用于调试与状态可视化
实现逻辑:
通过断言校验栈结构体指针非空且栈顶标记合法;打印提示信息后,从数组下标 top -1 开始遍历至下标0,依次输出所有有效元素;遍历结束后换行,保证输出格式整洁
四、扩展函数
1. 清空栈数据(不释放空间,top 归零)
c
// 清空数据:清空栈内所有有效元素,不释放内存
void STClear(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 将栈顶置为0,逻辑清空所有元素
ps->top = 0;
}
函数功能:清空栈内所有有效元素,将栈重置为空栈状态,保留已分配的内存空间不释放
实现逻辑:
首先通过断言校验栈结构体指针的合法性;仅将栈顶标记 top 赋值为 0,逻辑上清空所有有效元素,不修改数组数据与容量,不执行内存释放操作
2.获取栈总容量
c
// 获取栈总容量:返回栈当前最大可存储的元素数量
int STCapacity(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 返回栈的总容量
return ps->capacity;
}
函数功能:获取并返回当前动态顺序栈的最大存储容量,即已分配的数组空间可存储的元素总数
实现逻辑:
通过断言校验栈结构体指针非空,保证访问安全;直接返回结构体中capacity成员变量的值,无需计算与遍历
3. 反转栈内容
c
// 反转栈内容
void STReverse(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈元素个数合法(top 大于等于 0)
assert(ps->top >= 0);
// 左指针初始指向栈底
int left = 0;
// 右指针初始指向栈顶元素位置
int right = ps->top - 1;
// 双指针交换,实现栈内元素反转
while (left < right)
{
// 临时变量保存左侧元素
StackDataType tmp = ps->Data[left];
// 左右元素交换
ps->Data[left] = ps->Data[right];
// 把临时变量的值赋给右侧
ps->Data[right] = tmp;
// 左指针右移
left++;
// 右指针左移
right--;
}
}
函数功能:对动态顺序栈中的有效元素进行原地反转,改变元素的存储顺序
实现逻辑
通过断言校验栈结构体指针合法且栈顶标记非负;采用双指针法,左指针初始化为数组起始位置,右指针指向栈顶元素下标;循环交换双指针指向的元素,同步移动指针直至遍历完成,实现数组元素原地逆置
4. 查找值 x 是否在栈中,找到返回下标,没找到返回 -1
c
// 查找值 x 是否在栈中,存在返回对应下标,不存在返回 -1
int STFind(ST* ps, StackDataType x)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈不为空,栈中存在有效元素
assert(ps->top > 0);
// 遍历栈中所有有效元素,从栈底到栈顶依次查找
for (int i = 0; i < ps->top; i++)
{
// 如果找到目标值,返回当前元素的下标
if (ps->Data[i] == x)
{
return i;
}
}
// 遍历结束未找到目标值,返回 -1
return -1;
}
函数功能:在线性表结构的栈中顺序查找指定元素值,存在则返回对应数组下标,不存在则返回 - 1
实现逻辑:
通过断言校验栈指针合法且栈内包含有效元素;从栈底到栈顶遍历数组所有有效元素,逐一比对目标值,匹配成功立即返回当前下标;遍历完成未找到则返回 - 1 标识查找失败
5.获取栈中最小值
c
// 获取栈中最小值:遍历栈内所有元素,返回数值最小的元素
StackDataType STMin(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈不为空,存在有效元素
assert(ps->top > 0);
// 初始化最小值为栈底第一个元素
StackDataType Min = ps->Data[0];
// 从第二个元素开始遍历,依次比较更新最小值
for (int i = 1; i < ps->top; i++)
{
// 如果当前最小值大于遍历到的元素,更新最小值
if (Min > ps->Data[i])
{
Min = ps->Data[i];
}
}
// 返回栈中的最小值
return Min;
}
函数功能:遍历栈中所有有效元素,查找并返回数值最小的元素,用于栈数据的极值统计
实现逻辑:
通过断言校验栈指针合法且栈非空;将数组首个元素初始化为最小值,从第二个元素开始遍历所有有效数据,逐一比较并更新最小值变量;遍历完成后返回最终的最小值
6. 获取栈中最大值
c
// 获取栈中最大值:遍历栈内所有有效元素,返回其中的最大值
StackDataType STMax(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈不为空,存在有效元素
assert(ps->top > 0);
// 初始化最大值为栈中第一个元素
StackDataType Max = ps->Data[0];
// 从第二个元素开始遍历所有有效元素
for (int i = 1; i < ps->top; i++)
{
// 如果当前最大值小于遍历到的元素,更新最大值
if (Max < ps->Data[i])
{
Max = ps->Data[i];
}
}
// 返回栈中的最大值
return Max;
}
函数功能:遍历栈中所有有效元素,查找并返回数值最大的元素,用于栈数据的极值统计
实现逻辑:
通过断言校验栈结构体指针合法且栈非空;将数组首个元素初始化为最大值,从第二个元素开始线性遍历所有有效数据,逐一比较并更新最大值变量;遍历结束后返回最终最大值
7.批量弹出 n 个元素
c
// 批量弹出 n 个元素(批量出栈):一次性从栈顶移除 n 个元素
void STPopN(ST* ps, int n)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈内有有效元素
assert(ps->top > 0);
// 断言校验:保证弹出的元素个数不超过栈内有效元素个数,防止非法操作
assert(n <= ps->top);
// 栈顶直接减去 n,逻辑删除栈顶的 n 个元素
ps->top -= n;
}
函数功能:实现栈内指定数量元素的批量出栈,一次性逻辑移除栈顶的 n 个元素,不进行物理数据清理
实现逻辑:
通过三重断言保证操作安全,校验栈指针合法、栈非空、且批量弹出数量不超过有效元素总数;直接将栈顶标记 top 减去 n,完成批量逻辑出栈
8.复制栈:把 src 复制到 dest
c
// 将 src 栈的内容复制到 dest 栈(深拷贝,dest 会被重新初始化覆盖)
void STClone(ST* src, ST* dest)
{
// 断言校验:源栈指针不能为空
assert(!STEmpty(src));
// 断言校验:目标栈指针不能为空
assert(!STEmpty(dest));
// 断言校验:源栈有效元素个数合法(top >= 0)
assert(src->top >= 0);
// 先销毁/初始化目标栈,清空原有数据
STInit(dest);
// 遍历源栈所有元素,逐个入栈到目标栈
for (int i = 0; i < src->top; i++)
{
STPush(dest, src->Data[i]);
}
}
函数功能:将源栈src的所有有效元素深拷贝至目标栈dest,覆盖目标栈原有数据,实现两个栈的完全复制
实现逻辑:
通过断言校验源栈、目标栈指针合法且元素数量有效;先初始化目标栈以清空原有内容与内存;遍历源栈从栈底到栈顶的所有元素,调用入栈接口将元素逐个复制到目标栈,自动完成扩容与数据存储,最终实现完整拷贝
9.判断两个栈内容是否完全相同
c
// 判断两个栈内容是否一样:逐个比较元素与长度,完全相同返回true,否则返回false
bool STEqual(ST* st1, ST* st2)
{
// 断言校验:保证栈1指针不为空
assert(!STEmpty(st1));
// 断言校验:保证栈2指针不为空
assert(!STEmpty(st2));
// 断言校验:栈1有效元素个数合法(top >= 0)
assert(st1->top >= 0);
// 断言校验:栈2有效元素个数合法(top >= 0)
assert(st2->top >= 0);
// 第一步:元素个数不同,直接判定不相等
if (st1->top != st2->top)
{
return false;
}
// 第二步:个数相同,逐个下标比较元素内容
for (int i = 0; i < st1->top; i++)
{
// 发现任意一个元素不相等,直接返回false
if (st1->Data[i] != st2->Data[i])
{
return false;
}
}
// 所有元素都相同,返回true
return true;
}
函数功能:完整判断两个顺序栈的元素数量、对应位置元素值是否完全一致,返回布尔型比较结果
实现逻辑:
通过多重断言校验两个栈指针合法、栈顶标记非负;优先比较top值判断元素总数是否相等,不相等则直接返回false;总数一致时,从栈底到栈顶逐一下标遍历比对元素,任意位置不匹配立即返回false,全部匹配则返回true
五、全部代码
1. Stack.h
c
#pragma once // 防止头文件被重复包含
// 包含需要的库
#include <stdio.h> // 输入输出 printf
#include <stdlib.h> // malloc、realloc、free
#include <assert.h> // 断言,检查程序错误
#include <stdbool.h> // C语言布尔类型 true / false
// 栈存储的数据类型(这里是 int,想改类型只改这一行)
typedef int StackDataType;
// 栈结构体(数组实现的动态栈)
typedef struct Stack
{
StackDataType* Data; // 指向动态数组,真正存数据的地方
int top; // 栈顶标记:下一个要插入的位置下标
int capacity; // 栈当前最大容量(满了需要扩容)
} ST; // ST 是结构体别名,写代码更简洁
// ====================== 基础核心接口 ======================
// 初始化栈
void STInit(ST* ps);
// 销毁栈(释放内存)
void STDestroy(ST* ps);
// 入栈(把数据 x 压入栈顶)
void STPush(ST* ps, StackDataType x);
// 出栈(删除栈顶元素)
void STPop(ST* ps);
// 取栈顶元素(不删除)
StackDataType STTop(ST* ps);
// 获取栈中有效元素个数
int STSize(ST* ps);
// 判断栈指针是否为空(安全检查)
bool STEmpty(ST* ps);
// 判断栈是否已满(用于扩容)
bool STFull(ST* ps);
// ====================== 打印功能 ======================
// 打印栈(从栈底 → 栈顶)
void StackPrint_From_Bottom_to_Top(ST* ps);
// 打印栈(从栈顶 → 栈底)
void StackPrint_From_Top_to_Bottom(ST* ps);
// ====================== 高级扩展功能 ======================
// 清空栈数据(不释放空间,top 归零)
void STClear(ST* ps);
// 获取栈总容量
int STCapacity(ST* ps);
// 反转栈内容
void STReverse(ST* ps);
// 查找值 x 是否在栈中,找到返回下标,没找到返回 -1
int STFind(ST* ps, StackDataType x);
// 获取栈中最小值
StackDataType STMin(ST* ps);
// 获取栈中最大值
StackDataType STMax(ST* ps);
// 批量弹出 n 个元素
void STPopN(ST* ps, int n);
// 复制栈:把 src 复制到 dest
void STClone(ST* src, ST* dest);
// 判断两个栈内容是否完全相同
bool STEqual(ST* st1, ST* st2);
2.Stack.c
c
#include "Stack.h"
// 判断栈是否为空
// 参数:ps - 指向栈结构体的指针
// 返回值:指针为NULL返回true,否则返回false
bool STEmpty(ST* ps)
{
// 判断传入的栈结构体指针是否为NULL
if (ps == NULL)
{
// 指针为空,返回true
return true;
}
// 指针不为空,返回false
return false;
}
// 功能:判断当前栈是否已经存满元素
// 参数:ps 指向栈结构体的指针
// 返回值:栈满返回 true,栈未满返回 false
bool STFull(ST* ps)
{
// 判断条件:栈顶下标 top 等于 总容量 capacity
// 因为我们的 top 指向【栈顶元素的下一个位置】
// 所以当 top == capacity 时,说明数组已经没有空位可以存新元素
if (ps->top == ps->capacity)
{
// 条件成立 → 栈满,返回 true
return true;
}
// 条件不成立 → 栈未满,返回 false
return false;
}
// 初始化栈
// 参数:ps - 指向栈结构体的指针
void STInit(ST* ps)
{
// 断言:确保传入的栈指针不为空
assert(!STEmpty(ps));
// 将栈的数据区指针置空
ps->Data = NULL;
// 初始化栈顶下标和容量均为0
ps->top = ps->capacity = 0;
}
// 销毁栈
void STDestroy(ST* ps)
{
// 断言检查:确保传入的栈指针不为空
assert(!STEmpty(ps));
// 如果栈的数据指针不为NULL,说明已申请内存
if (ps->Data != NULL)
{
// 释放栈动态申请的内存
free(ps->Data);
}
// 将数据指针置空,防止野指针
ps->Data = NULL;
// 重置栈顶和容量为0,恢复栈的初始状态
ps->top = ps->capacity = 0;
}
// 扩容:检查栈空间是否已满,若已满则进行动态扩容
void STBuy(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 判断栈是否已满,满了则需要扩容
if (STFull(ps))
{
// 计算新容量:初始容量为0时设为4,否则扩容为原来的2倍
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
// 重新分配内存,扩容至新容量大小
StackDataType* tmp = (StackDataType*)realloc(ps->Data, newcapacity * sizeof(StackDataType));
// 判断内存分配是否成功
if (tmp == NULL)
{
// 打印扩容失败原因
perror("realloc failed");
return;
}
// 更新栈的数据指针指向新扩容的空间
ps->Data = tmp;
// 更新栈的容量为新容量
ps->capacity = newcapacity;
}
}
// 入栈:向栈中添加一个元素
void STPush(ST* ps, StackDataType x)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 扩容检查:如果栈空间不足,自动申请扩容
STBuy(ps);
// 将新元素放入当前栈顶位置
ps->Data[ps->top] = x;
// 栈顶上移,指向新的待插入位置
++ps->top;
}
// 出栈:删除栈顶的一个元素
void STPop(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈不为空,防止对空栈执行出栈操作
assert(ps->top > 0);
// 栈顶下移,逻辑删除栈顶元素
--ps->top;
}
// 取栈顶元素:获取栈顶的元素值,不删除元素
StackDataType STTop(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈不为空,防止获取空栈的栈顶元素
assert(ps->top > 0);
// 返回栈顶元素(栈顶下标为top-1)
return ps->Data[ps->top - 1];
}
// 获取栈中有效元素个数:返回栈里当前存储的元素数量
int STSize(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈顶下标合法,大于等于0
assert(ps->top >= 0);
// 返回栈顶值,即为有效元素个数
return ps->top;
}
// 打印栈(从栈底到栈顶)
void StackPrint_From_Bottom_to_Top(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈元素个数合法(top 大于等于 0)
assert(ps->top >= 0);
// 打印栈遍历方向提示信息
printf("From_Bottom_to_Top:");
// 循环遍历栈中所有有效元素,从栈底下标0开始到栈顶下标top-1结束
for (int i = 0; i < ps->top; i++)
{
// 逐个打印栈内的元素,元素之间用空格分隔
printf("%d ", ps->Data[i]);
}
// 打印换行符,使输出格式整洁,结束本次打印
printf("\n");
}
// 打印栈(从栈顶到栈底)
void StackPrint_From_Top_to_Bottom(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈元素个数合法(top 大于等于 0)
assert(ps->top >= 0);
// 打印栈遍历方向提示信息
printf("From_Top_to_Bottom:");
// 循环遍历栈中所有有效元素,从栈顶下标 top-1 开始到栈底下标 0 结束
for (int i = ps->top - 1; i >= 0; i--)
{
// 逐个打印栈内的元素,元素之间用空格分隔
printf("%d ", ps->Data[i]);
}
// 打印换行符,使输出格式整洁,结束本次打印
printf("\n");
}
// 清空数据:清空栈内所有有效元素,不释放内存
void STClear(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 将栈顶置为0,逻辑清空所有元素
ps->top = 0;
}
// 获取栈总容量:返回栈当前最大可存储的元素数量
int STCapacity(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 返回栈的总容量
return ps->capacity;
}
// 反转栈内容
void STReverse(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈元素个数合法(top 大于等于 0)
assert(ps->top >= 0);
// 左指针初始指向栈底
int left = 0;
// 右指针初始指向栈顶元素位置
int right = ps->top - 1;
// 双指针交换,实现栈内元素反转
while (left < right)
{
// 临时变量保存左侧元素
StackDataType tmp = ps->Data[left];
// 左右元素交换
ps->Data[left] = ps->Data[right];
// 把临时变量的值赋给右侧
ps->Data[right] = tmp;
// 左指针右移
left++;
// 右指针左移
right--;
}
}
// 查找值 x 是否在栈中,存在返回对应下标,不存在返回 -1
int STFind(ST* ps, StackDataType x)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈不为空,栈中存在有效元素
assert(ps->top > 0);
// 遍历栈中所有有效元素,从栈底到栈顶依次查找
for (int i = 0; i < ps->top; i++)
{
// 如果找到目标值,返回当前元素的下标
if (ps->Data[i] == x)
{
return i;
}
}
// 遍历结束未找到目标值,返回 -1
return -1;
}
// 获取栈中最小值:遍历栈内所有元素,返回数值最小的元素
StackDataType STMin(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈不为空,存在有效元素
assert(ps->top > 0);
// 初始化最小值为栈底第一个元素
StackDataType Min = ps->Data[0];
// 从第二个元素开始遍历,依次比较更新最小值
for (int i = 1; i < ps->top; i++)
{
// 如果当前最小值大于遍历到的元素,更新最小值
if (Min > ps->Data[i])
{
Min = ps->Data[i];
}
}
// 返回栈中的最小值
return Min;
}
// 获取栈中最大值:遍历栈内所有有效元素,返回其中的最大值
StackDataType STMax(ST* ps)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈不为空,存在有效元素
assert(ps->top > 0);
// 初始化最大值为栈中第一个元素
StackDataType Max = ps->Data[0];
// 从第二个元素开始遍历所有有效元素
for (int i = 1; i < ps->top; i++)
{
// 如果当前最大值小于遍历到的元素,更新最大值
if (Max < ps->Data[i])
{
Max = ps->Data[i];
}
}
// 返回栈中的最大值
return Max;
}
// 批量弹出 n 个元素(批量出栈):一次性从栈顶移除 n 个元素
void STPopN(ST* ps, int n)
{
// 断言校验:保证传入的栈指针不为空
assert(!STEmpty(ps));
// 断言校验:保证栈内有有效元素
assert(ps->top > 0);
// 断言校验:保证弹出的元素个数不超过栈内有效元素个数,防止非法操作
assert(n <= ps->top);
// 栈顶直接减去 n,逻辑删除栈顶的 n 个元素
ps->top -= n;
}
// 将 src 栈的内容复制到 dest 栈(深拷贝,dest 会被重新初始化覆盖)
void STClone(ST* src, ST* dest)
{
// 断言校验:源栈指针不能为空
assert(!STEmpty(src));
// 断言校验:目标栈指针不能为空
assert(!STEmpty(dest));
// 断言校验:源栈有效元素个数合法(top >= 0)
assert(src->top >= 0);
// 先销毁/初始化目标栈,清空原有数据
STInit(dest);
// 遍历源栈所有元素,逐个入栈到目标栈
for (int i = 0; i < src->top; i++)
{
STPush(dest, src->Data[i]);
}
}
// 判断两个栈内容是否一样:逐个比较元素与长度,完全相同返回true,否则返回false
bool STEqual(ST* st1, ST* st2)
{
// 断言校验:保证栈1指针不为空
assert(!STEmpty(st1));
// 断言校验:保证栈2指针不为空
assert(!STEmpty(st2));
// 断言校验:栈1有效元素个数合法(top >= 0)
assert(st1->top >= 0);
// 断言校验:栈2有效元素个数合法(top >= 0)
assert(st2->top >= 0);
// 第一步:元素个数不同,直接判定不相等
if (st1->top != st2->top)
{
return false;
}
// 第二步:个数相同,逐个下标比较元素内容
for (int i = 0; i < st1->top; i++)
{
// 发现任意一个元素不相等,直接返回false
if (st1->Data[i] != st2->Data[i])
{
return false;
}
}
// 所有元素都相同,返回true
return true;
}