数据结构——栈


概念

是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素的操作,进行数据的插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则

压栈:栈的插入操作叫做进栈/压栈/入栈,入的数据在栈顶

出栈:栈的删除操作叫做出栈,出的数据也在栈顶

栈的结构就像平常所见的羽毛球桶一样,从顶端放入羽毛球,从顶端取出羽毛球

如下图,入栈的顺序是1,2,3,那么出栈顺序就是3,2,1
栈的结构示意图
栈可以使用链表实现,为了方便理解,降低难度,这篇文章中我们使用数组实现

如果想尝试链表实现但还不会链表,可以看我之前写的这篇文章:数据结构——链表

接下来,我们用代码来实现栈的结构以及它的相关操作


代码实现

栈结构

栈的内部,有一个数组用于存放数据,因为栈是可以直接获取顶端元素的,因此还要有一个记录元素个数的整型,为了实现数组的扩容,还需要一个整型用于记录当前数组的容量

typedef int STDataType;//方便修改储存的数据类型

typedef struct Stack
{
	STDataType* a;//保存数据
	int top;//记录数据个数
	int capacity;//记录数组容量
}ST;

初始化

对栈的结构进行初始化

函数的接口处传入栈的指针
1.对栈的指针断言,如果指针为空则结束程序
2.将数组置空
3.将元素个数和容量初始化为0

void STInit(ST* pst)
{
	assert(pst);

	pst->a = NULL;
	//top指向栈顶元素的下一个数据
	pst->top = 0;
	pst->capacity = 0;
}

相信你已经看到上面代码注释中,top指向栈顶元素的下一个数据,这是为什么呢?
我们在前面讲到,栈使用数组保存数据,top记录数据个数,而数组元素下标是从0开始的。如果top指向栈顶元素,那么当top=0时,top指向数组首元素(即此时有栈顶数据),但是数据个数为0,两者相悖。

因此在这里规定top指向栈顶的下一个位置,那么栈顶应该为top-1


销毁

对栈的内存占用进行销毁

函数的接口传入栈的指针
1.断言栈指针
2.将数组置空
3.将元素个数和容量置0

void STDestroy(ST* pst)
{
	assert(pst);

	pst->a = NULL;
	pst->top = pst->capacity = 0;
}

入栈

从栈顶压入数据

扩容

用newcapacity保存新的容量,如果容量为0那么新容量赋值为4,否则容量扩大为原来的2倍

为了平衡内存和使用性能,一般扩容的倍数是1.5或2倍

int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;

STDataType* tmp = (STDataType*)realloc(pst->a, newcapacity *sizeof(STDataType));


入栈函数

函数接口传入栈指针和新数据x
1.断言栈指针
2.判断元素个数是否和容量相等,如果相等进行扩容
3.判断tmp是否为空指针,如果为空说明空间开辟失败
4.将新容量和空间赋值给capacity和a
5.将x赋值给栈顶的下一个位置
6.元素个数top加1

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, newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		pst->a = tmp;
		pst->capacity = newcapacity;
	}

	pst->a[pst->top] = x;
	pst->top++;
}

出栈

删除栈顶元素

函数接口传入栈指针
1.断言栈指针
2.断言元素个数,如果个数为0则不能出栈
3.元素个数top减1,此时无法访问栈顶数据,相当于栈顶被删除

void STPop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);

	pst->top--;
}

取栈顶

获取栈顶数据

函数接口传入栈指针,返回栈顶数据
1.断言栈指针和元素个数
2.将栈顶元素返回

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

	return pst->a[pst->top - 1];
}

判空

判断栈是否为空

函数接口传入栈指针,返回bool类型值
1.断言栈指针
2.直接返回比较结果

bool STEmpty(ST* pst)
{
	assert(pst);

	return pst->top == 0;
}

获取数据个数

 
函数接口传入栈指针,返回整型
1.断言栈指针
2.直接返回栈的数据个数

int STSize(ST* pst)
{
	assert(pst);

	return pst->capacity;
}

注意

在对结构体或类进行操作时,即使像获取个数、判空这种一行可以实现的功能,也不要直接访问,而是通过函数实现
例如下面的代码就是一种危险操作:

ST stack;
if(stack.capacity == 0)//直接对结构体进行访问
{
	printf("栈为空\n");
}

没有函数内部加以限制,直接访问可能会错误修改数据或者访问的并不是想要的数据

因此为了提高安全性,一定要通过封装好的方法函数访问内部数据


以上是栈的基本结构和常用的栈操作
如果存在错误,欢迎指正
  • 18
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值