数据结构-栈及其实现

目录

一.栈的概念

二.栈的实现

1.基本结构

2.常见接口

1.StackInit函数-对栈结构进行初始化

2.StackPush函数-入栈,即向栈顶内存放数据

3.StackPop函数-出栈,即删除栈顶元素

4.StackTop函数-获取栈顶元素

5.StackSize函数-获取栈的大小,即有效数据个数

6.StackEmpty函数-判断栈是否为空

7.StackDestroy函数-栈的销毁


一.栈的概念

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

栈顶(Top):线性表允许进行插入和删除的一端。

栈底(Bottom):固定的,不允许进行插入和删除的另一端。

空栈:不含任何元素。

二.栈的实现

我们之前学习过两种常用线性表,即顺序表和链表。这两种数据结构都能被我们用于实现栈。由于栈只需要在栈顶进行插入和删除元素,采用顺序表实现的话,把顺序表尾部作为栈顶,即对应顺序表的尾插和尾删,其时间复杂度为O(1)。 而采用链表实现的话,则需要把链表头作为栈顶,对应链表的头插和头删,其时间复杂度也为O(1)。为了使栈更加实用,我们通常采用动态开辟的空间来存放数据。

下面给出栈的顺序表实现:

1.基本结构

typedef int STDataType;
typedef struct Stack
{
	STDataType* _a;
	int _top;		// 栈顶
	int _capacity;  // 容量 
}Stack;

其中STDataType为重定义数据类型,这样定义有利于我们通过简便的修改,达到使栈存放不同类型数据的目的。

变量_a-指向动态开辟空间的指针,以实现动态内存开辟存放数据

变量_top-为栈顶位置。一般实现为空间内存放有效数据的个数。因此作为下标,其指向末尾元素的下一个元素的位置。

变量_capacity-为空间容量。若_capacity<=_top,想要继续添加数据,则对空间进行扩容。

2.常见接口

// 初始化栈 
void StackInit(Stack* ps);
// 入栈 
void StackPush(Stack* ps, STDataType data);
// 出栈 
void StackPop(Stack* ps);
// 获取栈顶元素 
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数 
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps);
// 销毁栈 
void StackDestroy(Stack* ps);

 1.StackInit函数-对栈结构进行初始化

这里需要我们先创建一个栈结构变量,再调用StackInit函数传入参数对其进行初始化。也可实现为StackCreate函数-在函数内利用动态内存开辟完成对栈空间指针的创建,并在进行初始化后返回该指针,这里我们介绍前者。

void StackInit(Stack* ps)
{
	assert(ps);
	ps->_a = NULL;
	ps->_top = ps->_capacity = 0;
}

首先由于传入形参为指针,故先用assert进行断言,防止其为空指针(后续函数实现也均需在开头进行断言)。然后对前文定义的结构体内变量_a,_top,_capacity进行初始化。由于尚未使用动态内存分配开辟空间,因此自然初始化_a为NULL,_top和_capacity均为0。

2.StackPush函数-入栈,即向栈顶内存放数据

void StackPush(Stack* ps, STDataType data)
{
	assert(ps);
	if (ps->_top == ps->_capacity)
	{
		int newCapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;
		STDataType* tmp = (STDataType*)realloc(ps->_a,sizeof(STDataType)*newCapacity);
		if (ps->_a == NULL)
		{
			printf("realloc error\n");
			exit(-1);
		}
        ps->a = tmp;
		ps->_capacity = newCapacity;
	}

	ps->_a[ps->_top] = data;
	ps->_top++;
}

首先我们需要判断是否有足够空间存放数据,即_capacity是否大于_top,若不大于,我们需要调用realloc函数进行空间扩容。为了避免频繁进行扩容,这里扩容实现为将原来的空间扩大为2倍,即newCapacity=2*ps->capacity。但由于我们初始化_capacity=0,因此在第一次扩容时扩大为2倍是有问题的,此处使用三目操作符进行判断,若_capacity=0,则赋值其为4。若扩容失败,则直接返回错误信息。其对应代码如下:

int newCapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;
STDataType* tmp= (STDataType*)realloc(ps->_a,sizeof(STDataType)*newCapacity);
if (ps->_a == NULL)
{
	printf("realloc error\n");
	exit(-1);
}
ps->_a = tmp;
ps->_capacity = newCapacity;

若扩容成功,则将data压入栈顶:ps->_a[ps->top]=data ,然后有效数据个数_a自增1。

3.StackPop函数-出栈,即删除栈顶元素

void StackPop(Stack* ps)
{
	assert(ps);
	assert(ps->_top);
	
	ps->_top--;
}

和顺序表一样,要删除末尾元素,只需要将有效数据个数_top自减1。当然首先我们得判断栈是否为空栈,这里也用断言实现。

4.StackTop函数-获取栈顶元素

STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(ps->_top);

	return ps->_a[ps->_top - 1];
}

两个断言不必多说,只需要返回栈顶元素的值就行了(_top-1为栈顶元素的下标)

5.StackSize函数-获取栈的大小,即有效数据个数

虽然我们能通过_top直接进行访问,但在实际使用中,我们一般将其封装为函数。

int StackSize(Stack* ps)
{
	assert(ps);
	return ps->_top;
}

非常简单,直接返回_top的值

6.StackEmpty函数-判断栈是否为空

bool StackEmpty(Stack* ps)
{
	assert(ps);
	return ps->_top==0;
}

ps->_top==0为条件表达式,若_top为0,条件表达式的值为真,即栈为空。若_top不为0,则条件表达式的值为假,即栈不为空。返回值为bool类型,在C语言中使用需要包含头文件stdbool.h。

7.StackDestroy函数-栈的销毁

void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->_a);
	ps->_a = NULL;
	ps->_top = ps->_capacity = 0;
}

只需要free(ps->_a)是否动态内存开辟的空间,然后将结构体变量置为初值即可。

栈的实现就介绍至此,感谢阅读。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值