【小白必看】栈的相关知识及实现

一、什么是栈

栈是一类特殊的线性表,栈只允许在一端进行插入和删除,允许插入和删除的一端叫做栈顶,另一端叫做栈底。栈的性质是:后进先出(Last In First Out)。

1.后进先出

  栈的性质是后进先出,那么什么是后进先出呢?顾名思义,就是后来的数据第一个跑出去。举个例子,小时候都玩过子弹枪吧,当你的小圆球子弹用光了之后,你总要添加子弹吧,那么最后一个添加的子弹正好在弹夹的最上面,也就第一个被发射出去,栈就是这样的。

2.栈的核心操作

  栈中的数据就像子弹一样可以被添加和发射,换个名词:压栈和出栈。

压栈:把数据存放到栈中的过程(添加子弹)。

出栈:将数据从栈中弹出的过程(发射子弹)

下面我来为大家用丑陋的绘画模拟压栈和出栈的过程

现在这个丑陋的没盖水桶就是我们的栈,在没有数据进入的时候,栈顶和栈底都是在一个位置上,旁边形状特异的球就是数据,现在我们要把球投进水桶里,也就是压栈。

现在我们已经把1号球扔了进去,栈顶也随着数据的压栈而改变,但是栈底是不会发生变化的。

很好,现在外面就剩一个小球了,但是我现在想把球扔出去,即数据的出栈,在数据出栈之后,栈顶也会随着下降,如图所示:

3.定义小结

从栈的定义和模拟栈数据的压栈出栈过程中,我们可以初步得出以下要点:

1.栈的结构是后进先出,即后进来的数据先被弹出。

2.在压栈的过程中,我们也可以在数据

没有压完时选择出栈,这样我们一组数据的出栈顺序就会变得多种多样。

3.在压栈和出栈的过程中,随着最上面的元素被压进或被取出,栈顶位置是在变化的,而栈底不变。

二、栈的基本操作

1.栈的定义

栈本质上就是线性表,只不过是操作受限而已,所以和顺序表,链表有着异曲同工之处,那么定义中都有哪些元素呢?

首先,栈有栈顶,栈底,这里栈顶我们用top来表示,栈底因为他一直没动,我们也懒得为他再命名一下,直接归0!其次,我上面模拟的这个大水桶总不可能一直往里面扔球吧,扔着扔着就满了,所以容量也是必不可少的,整体的数据存储我们用一个数组来表示。至此,栈的定义就呼之欲出了。

typedef struct Stack
{
    int* a;//承接数据的数组
    int top;//栈顶
    int capacity; //表示栈的容量
}ST;

在这里我们使用int的方式,也可以使用STDataType来表示,不过不要忘记声明一下。

typedef int STDataType;

 2.栈的初始化

我们回到最开始还没有数据进入的大水桶,可以看到里面是什么也没有的,但是栈其实做得更绝,初始化的时候连水桶边都不给你,容量直接设置为0,水桶刚出生就出来营业了。

具体实现如下:

void STInit(ST* pst)
{
//断言检测结构体传过来没有,栈还没出生怎么初始化啊
	assert(pst);
	pst->a = NULL;//数组清空
	pst->top = 0;//栈顶在最下面
	pst->capacity = 0;//刚出生的桶不许有容量
}

值得一提的是,断言assert函数的头文件是  #include<assert.h>

这样我们就完成了栈的初始化。

3.压栈(数据入栈)

现在我们来实现数据入栈的操作,既然要向桶里投球,肯定要先检查桶是不是满的,或者是不是刚出生的没有容量的,检测完了之后我们就可以放数据了,放完数据不要忘记栈顶位置也是随之改变的。如果没有容量我们可以动态扩容,直接让水桶强制成年。那么怎么进行扩容呢,首先我们先了解一个函数:realloc

在官网中,我们可以看到realloc里面有两个参数,第一个是我们要开辟空间的对象,第二个是要开辟空间的大小,下面我们用到我们的函数中。

首先,扩容的判断条件是什么?就是当我们水桶满了的时候,即top=capacity


//当栈顶位置到达容量时,需要进行扩容
	if (pst->top == pst->capacity)
	{
       //如果我们的初始容量是0,就把capacity设置为4,不是0就把初始容量扩大一倍
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
    //我们要进行扩容的是a数组,后面给出要扩容的大小
		int* tmp = (int*)realloc(pst->a, (newcapacity) * sizeof(int));
    //看是否扩容成功
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
    //更新容量
		pst->capacity = newcapacity;
	

 最后我们把数据放入数组中,然后进行栈顶位置的改变

void STPush(ST* pst, int x)
{
	assert(pst);
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		int* tmp = (int*)realloc(pst->a, (newcapacity) * sizeof(int));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newcapacity;
	}
	pst->a[pst->top] = x;
	pst->top++;
	}

4.出栈

出栈的时候,我们直接把栈顶的位置改变就可以了,但是我们改变的方式是--top,存在一定的风险,如果栈里没有元素,再减就会发生错误,所以我们要断言一下栈是否为空。

void STPop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));
	pst->top--;
}

5.判断栈是否为空

判断栈是否为空就十分简单了,我们只要看栈顶就行了,如果top为0就返回true,否则false

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

6.获取栈顶元素

有一个错误的思想就是觉得栈顶元素一定是a[pst->top],但是其实不是这样的,我们还是拿大水桶举例:

在最开始没有元素进入时栈顶的值为0,当进入一个元素时,top的值转而为1,但是实际上1此时是栈顶元素,所以栈顶元素其实是a[pst->top-1]

STDataType STTop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));
	return pst->a[pst->top-1];
}

7.获取栈元素的数量

这个就很简单了,直接返回top代码如下:

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

8.销毁栈

销毁栈要先释放数组,然后置空

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

}

三、总结

其实栈的实现非常简单,我们只需要理解他的出入栈方式即可实现,结合大水桶与小球,希望大家好好理解。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值