C语言--栈的创建及使用详解

1. 栈的基本概念

1.1 概念

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

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

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

1.2 结构

栈的基本结构如下:
在这里插入图片描述
在这里插入图片描述

最下面的部分为栈底,最上面的数据位置为栈顶。
当数据传入栈的时候,数据会直接放在栈顶的位置,此过程为压栈。
当取数据时,只能从栈顶的数据依次往下取,此过程为出栈。
栈结构导致了栈中的数据会遵从先进后出(后进先出)的原则。

1.3 栈结构的选择

栈结构只需要确保数据是先进后出的原则,因而栈的实现既可以用链表结构,也可以用顺序表结构。

考虑到栈结构主要是对栈顶进行频繁操作,如果采用链表结构,操作较为繁琐。采用顺序表结构,可以直接通过下标对数据进行操作,操作简便。因而本文采用顺序表结构进行栈的创建。

顺序表的头位置为栈底,表尾为栈顶。

2. top 位置的说明

因为栈的基本结构采用了顺序表的方式,当我们插入和访问数据时,都是从下标为0 的位置开始访问。此时,栈顶位置 top 有两种放置方式:

  1. top 记录最后一个数据位置的下一个位置
  2. top 记录最后一个数据的位置

对于第一种情况:top 初始化为0,当插入第一个数据后,top 变为 1 ,访问栈顶数据时,需要访问 top - 1 的位置,此位置才有数据。返回数据的长度时,只需要返回top 的值即可。判空时,只要判断top 是否为零即可。此方法便于判空、判断数据长度。

对于第二种情况:top 初始化为-1,当插入第一个数据后,top 变为 0 ,top 的值即为栈顶数据的位置。访问栈顶数据时,只需要访问 top 的位置。返回数据的长度时,需要返回top+1 的值。判空时,需要单独判断top 是否为 -1 。此方法不便于便于判空、判断数据长度。

本文采用第一种方法记录栈顶位置。

3. 栈的具体创建

3.1 结构体的创建

typedef int MySTDataType;
//宏定义顺序表中数据结构的类型,便于不同类型数据的接入
typedef struct MyStackNode
{
	MySTDataType* a;//存放数据
	int top;//栈顶位置,从0开始,每次指定到最后一个数据位置的下一个位置
	int capacity;//记录开辟空间的大小
}ST;

3.2 栈的初始化

void STInit(ST* pst)
{
	assert(pst);//断言,数据为空时报错

	pst->a = NULL;//置空
	pst->top = 0;//置空,确保栈顶在最后一个数据位置的下一个位置
	pst->capacity = 0;//置空
}

3.3 判空

当栈顶的位置位于起点时,此时栈为空,即 top = 0.

bool STEmpty(ST* pst)//布尔判断,为真时返回ture,为假时返回false。
{
	assert(pst);

	return pst->top == 0;//当top 为0时,满足条件,返回ture,否在返回false
	//等价于以下代码
	/*if(pst->top==0)
		return ture;
	else
	retrun false;*/
}

3.4 压栈

压栈要考虑空间的大小问题,当空间不足时,要及时开辟一个新的空间。

void STPush(ST* pst, MySTDataType x)
{
	if (pst -> top == pst->capacity)
	//此时栈顶位于空间最后一个位置,若还想插入新的数据,需要开辟新空间
	{

		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		//栈中无数据时,开辟4个字节大小,若有数据,则开辟原来大小的两倍
		MySTDataType* newnode = (MySTDataType*)realloc(pst->a, newcapacity * sizeof(MySTDataType));//开辟空间
		if (newnode == NULL)
		{
			perror("realloc fail\n");
			return;
		}
		pst->a = newnode;//新开辟的空间赋给结构体
		pst->capacity = newcapacity;//空间大小更新

	}
	pst->a[pst->top] = x;//在栈顶位置插入新的数据
	pst->top++;//栈顶移到下一个位置
}

3.5 出栈

出栈时,只需要将栈顶的数据排除到栈之外即可,即将栈顶的位置往下移动即可。

void STPop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));
	//断言,为空时你报错
	pst->top--;//移动栈顶位置,达到出栈的效果
}

3.6 返回栈顶值

栈顶位于最后一个数据的下一个位置,因为访问栈顶最后一个数据时,要让top - 1.

MySTDataType STTop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));

	return pst->a[pst->top-1];//返回栈顶的数据
}

3.7 栈中数据个数

栈顶top 始终在栈顶的位置,top 的值即为栈中数据的多少。

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

	return pst->top;//返回数据数目
}

3.8 栈的销毁

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

	free(pst->a);//释放开辟的空间
	//置空
	pst->a = NULL;
	pst->top = 0;
	pst->capacity = 0;
}

4. 栈的使用

4.1 代码使用

int main()
{
	ST st;
	
	STInit(&st);//初始化
	
	//插入数据1 ~ 5 
	STPush(&st, 1);
	STPush(&st, 2);
	STPush(&st, 3);
	STPush(&st, 4);
	STPush(&st, 5);

	while (!STEmpty(&st))//所有数据出栈之后结束循环
	{
		printf("%d ", STTop(&st));//打印栈顶数据
		STPop(&st);//使栈顶数据出栈
	}

	STDestroy(&st);
	return 0;
}

结果如下:实现了后进先出的目的
在这里插入图片描述

4.2 现实场景使用

1、函数调用
在程序中,每个函数调用都需要将当前状态的信息(比如函数调用前的参数、局部变量和程序计数器等)保存到栈中,等到函数调用结束后再从栈中弹出这些信息,恢复调用前的状态。这个过程被称作函数的压栈和弹栈操作。

2、表达式求值
通常我们在计算机中对表达式求值时都采用栈来实现。比如中缀表达式转后缀表达式的操作就需要使用栈。

3、系统调用
在操作系统中,内核通常会将一个系统调用的参数、返回值和程序计数器等状态保存到进程的用户栈中,在系统调用结束后再从栈中弹出这些信息,恢复调用前的状态。

4、缓存机制
缓存通常也使用栈的方式实现,被访问的数据最先进入栈顶,最后的则返回底部。例如:网页缓存

5、代码编辑器
用栈来判断括号是否成对,以及最近的括号匹配情况。若左括号,则入栈;若右括号,则将栈顶元素弹出,若是对应的左括号,则继续遍历;否则匹配失败。

5. 总结

栈是一种较为特殊的顺序表,只要掌握了顺序表,栈的创建相对来说时偏简单的。

  • 50
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值