数据结构:栈

1 概念

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

栈的插入操作也叫做进栈 / 入栈 / 压栈,其操作完成在栈顶。

栈的删除操作也叫做出栈。其操作完成也是在栈顶。

                

2 特点

只允许在一端进行数据的插入和删除操作(栈顶,其数据元素遵循先进后出(后进先出)原则。

3 实现

3.1 栈实现体系结构的确立

因其栈本质还为线性表,所以其实现既可以使用顺序表也可以使用链表。但其两者实现难度和复杂程度不同,两者其差异主要体现在对栈顶元素的删除上。

例如,链表,如果用单链表作为体系结构,那么在删除时单链表元素时,肯定需要知道该元素的前一个位置。

但单链表要知道某个元素的前一个位置比较麻烦(单向结构),要解决这个问题,可以多定义一个指针来保存元素的前一个位置,或者采用双向链表结构,就比较复杂了。

而对于用顺序表(数组)来说,我不需要知道要删除元素的前一个位置,我只需要知道要删除元素位置的下表即可,因此,对于栈的实现来说,我主要采用了动态的顺序表来进行实现。

3.2 一个具体的栈应该包括什么?

对于一个较为成熟的栈,具体应该包括栈的初始化,插入元素,删除元素,获取栈顶元素,获取栈内元素个数,判断栈是否为空,销毁栈这6大基本部分。

3.3 实现细节

对于栈的实现来说,其根本在于栈顶操作,对于其实现的栈来说,我选择在开辟数组元素时,多开辟一个容量,定义一个top下标进行数据的插入和删除操作。top指向的是栈顶的下一个位置。具体见下图。

当栈内一个元素都没有时,top此时下标为0,其位置指向是在栈顶的下一个位置。

当栈内元素(总共5个)满了时,此top下标数等于其栈内元素总个数。

以此判断栈内元素是为空还是满。并且top还可以记录栈内元素个数,插入就加1,删除就减一。

3.4 具体实现

3.4.1 结构体
typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;//开辟的数组总数
}Stack;
3.4.2 栈的初始化
void StackInit(Stack* ps)
{
	assert(ps);

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

初始化内容简单,assert()函数相当于控制语句,当ps等于空的时候,程序就终止了,并且会提示ps会空信息。也可以使用if()语句来进行控制。

3.4.3 栈的插入操作
void StackPush(Stack* ps, STDataType x)
{
	assert(ps);

	if (ps->top == ps->capacity)//当top下标等于开辟的数组总数时则需要扩容
	{
		int newcapacity = 0;
		if (ps->top == 0)//处理刚开始初始化时top下标为0的情况
		{
			newcapacity = 4;
		}
		else
		{
			newcapacity = ps->capacity * 2;
		}
		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");//和assert()函数意义差不多
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
	ps->a[ps->top] = x;//先给top下标位置元素赋值
	ps->top++;//之后再将top下标加1,指向下一个下标。
}

其插入操作相信各位帅哥,美女不难理解,就是如果空间满了,则开辟空间进行扩容。如果未满,则直接插入。

3.4.4 栈的删除操作
void StackPop(Stack* ps)
{
	assert(ps);
	assert(ps->top > 0);

	ps->top--;
}

删除操作则就更为容易,只需要将top下标位置减一即可,空间无须释放,因为可能还会再插入,释放了反而需要再进行开辟,较为麻烦。

需要注意的一点为,如果top下标此时为0,说明栈内是没有元素的,那么就不要去减一了,所以assert(ps->top > 0)相当于控制top,当top大于0时,才会执行下面的减一操作。也可以通过if()语句进行控制,本意都相同。

3.4.5 获取栈顶元素
STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(ps->top > 0);

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

获取栈顶元素也是较为简单,只需要注意的为,因其top下标指向的是栈顶的下一个元素(具体可见上图),所以栈顶为top - 1,也是需要注意top为0时就不能减一了。

3.4.6 判断栈是否为空
bool StackEmpty(Stack* ps)
{
	assert(ps);

	return ps->top == 0;
}

 借助前文分析,判断栈是否为空,我们只需要判断top下标是否为0即可。

3.4.7 获取栈内元素个数
int StackSize(Stack* ps)
{
	assert(ps);

	return ps->top;
}

注意,其实top记录的就是栈内元素个数总数,所以返回时直接返回top即可。而不是返回capacity,capacity为开辟的数组个数,不为元素个数。

3.4.8 栈的销毁
void StackDestroy(Stack* ps)
{
	assert(ps);

	free(ps->a);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}

对于销毁来说,我们只需要将申请的内存空间再返回给系统即可,所以我们只需要销毁掉申请的数组a,然后再将其他的变量回归到初始值即可。

综上,栈的分析到此结束,下一篇我们来讲下队列的具体操作等。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值