数据结构Stack(C语言--用数组实现栈)

一、栈

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* 类型指针是否为空指针,保障后续栈操作的安全性

实现逻辑:

  1. 函数接收栈结构体指针 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;
}

函数功能:判断当前动态顺序栈是否已满,为扩容操作提供判定依据

实现逻辑:

  1. 函数接收合法的栈结构体指针 ps,通过比较栈顶标记 top 与容量 capacity 的值,若二者相等,说明有效元素已占满全部存储空间,返回 true;否则返回 false

3.初始化栈

cpp 复制代码
// 初始化栈
// 参数:ps - 指向栈结构体的指针
void STInit(ST* ps)
{
	// 断言:确保传入的栈指针不为空
	assert(!STEmpty(ps));

	// 将栈的数据区指针置空
	ps->Data = NULL;

	// 初始化栈顶下标和容量均为0
	ps->top = ps->capacity = 0;
}

函数功能:完成栈结构的初始化,为后续栈操作建立合法初始状态。

实现逻辑:

  1. 合法性校验:通过 assert(!STEmpty(ps)) 断言校验传入的栈结构体指针非空,避免空指针访问,保证函数执行安全
  2. 成员初始化:将栈的动态数据数组指针 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;
}
相关推荐
发疯幼稚鬼2 小时前
大整数乘法运算
c语言·算法
宵时待雨2 小时前
C++笔记归纳17:哈希
数据结构·c++·笔记·算法·哈希算法
阿昭L4 小时前
Windows通用的C/C++工程CMakeLists
c语言·c++·windows·makefile·cmake
炘爚4 小时前
单链表如何逆置
数据结构
liuyao_xianhui4 小时前
优选算法_栈_删除字符中的所有相邻重复项_C++
开发语言·数据结构·c++·python·算法·leetcode·链表
always_TT4 小时前
C语言中的布尔值:_Bool与stdbool.h
c语言·开发语言
WolfGang0073214 小时前
代码随想录算法训练营 Day22 | 回溯算法 part04
数据结构·算法
罗湖老棍子5 小时前
花神游历各国(信息学奥赛一本通- P1550)(洛谷-P4145)
数据结构·算法·线段树·势能数·区间开平方根 区间查询
寒秋花开曾相惜6 小时前
(学习笔记)3.8 指针运算(3.8.5 变长数组)
java·c语言·开发语言·笔记·学习