栈的实现指南

栈(Stack)

是一种重要的数据结构,它具有以下特点和用途:

  1. 后进先出(LIFO)原则:栈的最基本特性是后进先出,也就是最后添加到栈中的元素会是第一个被移除的元素。这种特性让栈非常适合处理那些需要按顺序记录然后逆序处理的数据。

  2. 操作限制:栈主要支持两种操作:压栈(push),即在栈顶添加一个元素;和弹栈(pop),即移除栈顶的元素。还可以进行的操作包括查看栈顶元素(peek/top)和检查栈是否为空。

  3. 应用广泛:栈在计算机科学中有广泛的应用,如在算法中进行递归调用的处理、程序的函数调用堆栈、表达式求值(例如计算器中的操作)、回溯算法(如迷宫求解、深度优先搜索等)等。

  4. 简单高效:由于栈的操作非常简单(只在一个端点操作),它的各种操作通常都非常高效,大部分时间复杂度为 O(1),即常数时间。

  5. 内存管理:在一些编程语言中,栈用于管理局部变量的内存。这些变量只在函数调用期间存在,并在函数调用结束时自动被移除。

  6. 实现方式:栈可以通过数组或链表实现。数组实现的栈具有固定的大小,而链表实现的栈则可以动态地增长。

下面我们来实现这个栈吧

头文件

#pragma once

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>


typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;		// 标识栈顶位置的
	int 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);

这段代码是一个用C语言编写的栈(Stack)的实现。首先,我们看到包含了几个头文件:stdio.h 用于标准输入输出,assert.h 用于断言,stdlib.h 提供了动态内存分配、随机数生成等功能,而 stdbool.h 用于处理布尔类型。

接下来定义了一个数据类型 STDataType,在这个代码中它被定义为 int 类型,用于表示栈中存储的数据类型。

然后,定义了一个 Stack 结构体,这个结构体有三个成员:一个指向 STDataType 类型的指针 a(用于存储栈的数据),一个整型 top(用于标识栈顶的位置),以及一个整型 capacity(表示栈的容量)。

之后是关于栈的一系列操作函数:

  • STInit(ST* pst):初始化栈。
  • STDestroy(ST* pst):销毁栈,释放资源。
  • STPush(ST* pst, STDataType x):向栈顶插入一个元素。
  • STPop(ST* pst):从栈顶删除一个元素。
  • STTop(ST* pst):获取栈顶元素。
  • STEmpty(ST* pst):检查栈是否为空。
  • STSize(ST* pst):返回栈中元素的数量。

初始化栈

void STInit(ST* pst) {
	pst->a = NULL;
	pst->top = 0;
	pst->capacity = 0;

}
  1. 参数:函数接受一个指向 ST 结构体的指针 pst 作为参数。这意味着它操作的是一个已经存在的 ST 类型的实例。

  2. 初始化 apst->a = NULL; 这一行将栈的数据指针设置为 NULL。这表示在初始化时,栈是空的,没有分配任何内存来存储数据。

  3. 初始化 toppst->top = 0; 这行代码将栈顶指标 top 设置为 0。在栈的上下文中,top 通常用来表示下一个可插入元素的位置,或当前栈顶元素的位置。在初始化时,由于栈是空的,所以 top 被设置为 0

  4. 初始化 capacitypst->capacity = 0; 这行代码将栈的容量设置为 0。这意味着在初始化阶段,栈没有分配任何内存来存储数据,因此其容量为零。

栈的销毁

void STDestroy(ST* pst) {
	assert(pst);
	free(pst->a);
	pst->top = 0;
	pst->capacity = 0;

}
  1. 断言检查assert(pst); 这一行是一个安全检查,确保传入的栈指针 pst 不是 NULL。如果 pstNULL,这个断言将失败,并导致程序终止。这是一种常见的调试手段,用于在开发阶段捕捉错误。

  2. 释放内存free(pst->a); 这行代码释放了栈 pst 的数据数组 a 所占用的内存。在栈初始化或在其生命周期中通过动态内存分配函数(如 malloc, realloc 等)分配了内存,那么在销毁栈的时候就需要使用 free 函数来释放这块内存。这是防止内存泄漏的关键步骤。

  3. 重置栈的状态:随后的两行代码 pst->top = 0;pst->capacity = 0; 将栈的顶部索引和容量重置为 0。这意味着栈被视为一个空栈,没有元素,也没有为存储元素分配内存空间。

栈顶插入和删除

// 栈顶插入删除
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, sizeof(STDataType) * newcapacity);
		if (tmp == NULL) {
			perror("realloc failed");
			return;
		}
		pst->a = tmp;
		pst->capacity = newcapacity;

	}
	pst->a[pst->top] = x;
	pst->top++;
}
//尾删,栈顶删除
void STPop(ST* pst) {
	assert(pst);
	assert(pst->top > 0);
	pst->top--;
}

STPush 函数

STPush 函数用于向栈中插入一个新元素。它的具体工作流程如下:

  1. 断言检查:使用 assert(pst) 确保传入的栈指针 pst 不是 NULL

  2. 容量检查和扩展

    • 首先检查栈是否已满(即 pst->top == pst->capacity)。如果已满,需要扩展栈的容量。
    • 计算新容量:如果当前容量是 0,则新容量设为 4;否则,将容量加倍。
    • 使用 realloc 函数调整栈的存储空间大小。realloc 尝试重新分配内存块,并将其扩展到新的大小。
    • 如果 realloc 返回 NULL,表示内存分配失败。这时会打印错误信息,并提前返回,不会执行插入操作。
    • 更新栈的存储空间指针 pst->a 和容量 pst->capacity
  3. 插入元素:将新元素 x 放置在栈顶的位置,即 pst->a[pst->top]

  4. 更新栈顶位置:增加 pst->top 的值,以指向下一个空闲位置。

STPop 函数

STPop 函数用于从栈中删除栈顶元素。其操作较为简单:

  1. 断言检查

    • 使用 assert(pst) 确保传入的栈指针 pst 不是 NULL
    • 使用 assert(pst->top > 0) 确保栈中至少有一个元素可供删除。
  2. 删除元素:通过减少 pst->top 的值来实现删除操作。在栈中,这通常意味着仅仅减少指向栈顶元素的指针或索引。实际上并没有从内存中删除元素,但被视为从栈中移除了。

栈顶元素的读取

STDataType STTop(ST* pst) {
	assert(pst);
	assert(pst->top > 0);

	return pst->a[pst->top-1];
}
  1. 断言检查

    • assert(pst); 这个断言确保传入的栈指针 pst 不是 NULL。如果 pstNULL,程序将因断言失败而终止。这是一种防御性编程手段,用于捕捉程序中的错误。
    • assert(pst->top > 0); 这个断言确保栈不为空(即栈中至少有一个元素)。在空栈上尝试获取栈顶元素是不合法的操作,所以如果 pst->top <= 0,程序同样会因断言失败而终止。
  2. 获取栈顶元素return pst->a[pst->top-1]; 这行代码返回栈顶的元素。由于 pst->top 指向下一个可插入元素的位置,因此栈顶元素实际上位于 pst->top-1 的位置。

检查栈是否为空

bool STEmpty(ST* pst) {
	assert(pst);
	return pst->top == 0;
}

检查栈是否为空return pst->top == 0; 这行代码检查栈的 top 成员是否为 0。在栈的上下文中,top 表示栈顶的位置,也就是下一个可插入元素的位置。如果 top0,这意味着栈中没有任何元素,即栈为空。函数返回一个布尔值:如果栈为空,则返回 true;否则返回 false

栈的大小判定

int STSize(ST* pst) {
	assert(pst);
	return pst->top;
}

返回栈的大小return pst->top; 这行代码返回栈的 top 成员的值。在这个栈的实现中,top 成员不仅代表栈顶元素的位置,也隐含地表示了栈中元素的数量。因为每次添加元素时,top 的值会增加,每次移除元素时,top 的值会减少。

测试


int main()
{
	ST s;
	STInit(&s);
	STPush(&s, 1);
	STPush(&s, 2);
	STPush(&s, 3);
	printf("%d ", STTop(&s));
	STPop(&s);
	printf("%d ", STTop(&s));
	STPop(&s);

	STPush(&s, 4);
	STPush(&s, 5);


	while (!STEmpty(&s))
	{
		printf("%d ", STTop(&s));
		STPop(&s);
	}
	printf("\n");

	return 0;
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值