【数据结构】栈---超详解

目录

一.🚀前言🚀

二.🚀栈🚀

1.💠栈的概念💠

2.💠压栈💠

3.💠出栈💠

4.💠栈的特性💠

三.🚀栈内各函数的实现🚀

1.💠定义栈💠

2.💠栈的初始化💠

3.💠销毁栈💠

4.💠压栈💠

5.💠出栈💠

6.💠判空💠

7.💠获取栈顶元素💠

8.💠获取栈内元素个数💠

四.🚀栈的整体实现🚀

1.💠Stack.h💠

2.💠Stack.c💠

3.💠test.c💠

刹国(结束)!!!!


一.🚀前言🚀

前面我们讲解了顺序表,单链表以及双链表,接下来我们将进入另一种数据结构的学习---栈。注意,这里说的栈是数据结构的一种,而不要和栈区混淆。栈是一种数据结构,而栈区是内存中划分的一个区域。

二.🚀栈🚀

1.💠栈的概念💠

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。

2.💠压栈💠

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

3.💠出栈💠

栈的删除操作叫做出栈。出数据时同样是在栈顶出。

4.💠栈的特性💠

栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。先压入栈中的数据后出栈。


三.🚀栈内各函数的实现🚀

本章栈是以顺序表为基础实现的,顺讯表即数组

1.💠定义栈💠

//数组栈
//定义类型
typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;
}Stack;

栈由顺序表实现,因此需要一个数组,而栈有容量大小以及需要获取栈顶数据,所以需要同时管理多种不同类型数据,进而用一个结构体进行管理刚刚好。typedef将数据类型int以及结构体进行了重定义,以便于后续书写及修改。


2.💠栈的初始化💠

//初始化栈
void STInit(Stack* pst)
{
	assert(pst);
	pst->a = NULL;
	pst->capacity = 0;
	pst->top = 0;
}

assert对栈地址进行断言,防止传参时失误传为空指针,然后将数组a置为空,容量以及栈顶赋初值为0。严格来说,这里的top其实并不是指向栈顶的,而是指向栈顶的下一个位置,因为当进行压栈操作时,需要往top位置放入数据,而放入数据后需要将top++,以便于后续压栈,因此top实际上是栈顶元素的下一个位置。


3.💠销毁栈💠

//销毁栈
void STDestroy(Stack* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->capacity = 0;
	pst->top = 0;//top是栈顶数据的下一个位置
}

栈的销毁和栈的初始化及其相似,不同之处在于栈的销毁需要将数组的空间进行释放,其余操作都一样。数组a是动态内存开辟出来的,因此需要用free进行释放,以免造成内存泄漏。数组a是如何开辟出来的后面会进行介绍。


4.💠压栈💠

//压栈
void STPush(Stack* pst, STDataType x)
{
	assert(pst);
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;
		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++;
}

在进行压栈前,都必须先对栈的容量进行判断,而一旦栈的容量已满就需要考虑进行扩容,这样才会有足够多的空间压栈。

很多小伙伴在之前初始化的时候或许就有这么一个疑惑,初始化的时候不应该起码开辟一个带有一定空间大小的数组吗?要不然刚开始的时候那些数据放在哪儿?

其实,这是博主埋的一个伏笔,这就是上面这个代码的奇妙之处,用一个三目操作符以及realloc动态内存函数就很好的解决了这一问题,初始化时我们将top以及capacity和a都初始化为了空。因此当我们插入数据时,第一次进来就会开辟数组的基础大小,第二次进来满足条件才会进行扩容。这里值得一说的是,realloc在第一个参数即pst->a为空时,其功能和malloc是一样的,所以这段代码很好的解决了栈的初始大小开辟以及扩容的问题。


5.💠出栈💠

//出栈
void STPop(Stack* pst)
{
	assert(pst);
	assert(!STEmpty(pst));
	pst->top--;
}

出栈非常简单,前面说过,出栈出的是栈顶元素,因此主需要将top--即可。出栈其实就是删除栈顶元素,栈内实际有效数据其实就是下标0到top - 1这个范围,因此当将top--后,栈的实际有效数据范围也就减小了,栈顶元素也就相当于被删除了。STEmpty(pst)为判空函数,如何实现后面介绍,这里主要是预防当栈为空后,继续对栈进行删除。


6.💠判空💠

//判空
bool STEmpty(Stack* pst)
{
	assert(pst);
	return pst->top == 0;
}

bool是一种数据类型,简单来说就是用bool做桉树返回类型时,函数可以返回true或是false。如果栈为空返回true,不为空返回false。当top为0时,pst->top == 0表达式结果为真,函数返回true,否则返回false。


7.💠获取栈顶元素💠

//获取栈顶数据
STDataType STTop(Stack* pst)
{
	assert(pst);
	assert(!STEmpty(pst));
	return pst->a[pst->top - 1];
}

同样栈为空时,无法获取栈顶元素。先前说过,top其实是栈顶元素的下一个位置,所以想要获取栈顶数据,还需将top减1才行。


8.💠获取栈内元素个数💠

//栈内元素个数
int STSize(Stack* pst)
{
	assert(pst);
	return pst->top;
}

我们知道数组的下标是从0开始的,而top是栈顶元素的下一个位置,刚好就是栈内元素个数的大小。


四.🚀栈的整体实现🚀

1.💠Stack.h💠

包含头文件及类型和函数的声明

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>


//数组栈
//定义类型

typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;
}Stack;

//初始化栈
void STInit(Stack* pst);

//销毁栈
void STDestroy(Stack* pst);

//压栈
void STPush(Stack* pst, STDataType x);

//出栈
void STPop(Stack* pst);

//判空
bool STEmpty(Stack* pst);

//获取栈顶数据
STDataType STTop(Stack* pst);

//栈内元素个数
int STSize(Stack* pst);

2.💠Stack.c💠

函数功能实现

#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"

//初始化栈
void STInit(Stack* pst)
{
	assert(pst);
	pst->a = NULL;
	pst->capacity = 0;
	pst->top = 0;
}

//销毁栈
void STDestroy(Stack* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->capacity = 0;
	pst->top = 0;//top是栈顶数据的下一个位置
}

//压栈
void STPush(Stack* pst, STDataType x)
{
	assert(pst);
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;
		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++;
}

//出栈
void STPop(Stack* pst)
{
	assert(pst);
	assert(!STEmpty(pst));
	pst->top--;
}

//判空
bool STEmpty(Stack* pst)
{
	assert(pst);
	return pst->top == 0;
}

//获取栈顶数据
STDataType STTop(Stack* pst)
{
	assert(pst);
	assert(!STEmpty(pst));
	return pst->a[pst->top - 1];
}

//栈内元素个数
int STSize(Stack* pst)
{
	assert(pst);
	return pst->top;
}

3.💠test.c💠

测试函数逻辑及功能

#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"

void TestStack()
{
	Stack st;
	STInit(&st);
	STPush(&st, 1);
	STPush(&st, 2);
	STPush(&st, 3);
	STPush(&st, 4);
	STPush(&st, 5);
	STPop(&st);
	STPop(&st);
	printf("Size:%d", STSize(&st));
	printf("\n");
	while (!STEmpty(&st))
	{
		printf("%d ", STTop(&st));
		STPop(&st);
	}
	STDestroy(&st);
}

int main()
{
	TestStack();
	return 0;
}

本篇以栈的实现为主题的博客到此就要结束了,希望本篇博客可以帮助到各位小伙伴,博主码字不易,如果本篇博客对你有所帮助,还望各位小伙伴点赞👍,收藏⭐+关注,感谢各位的支持!如果有什么不明白的地方或是另有其他的高见,也可以在评论区留言,博主也会及时回复或进行更改,欢迎指正,谢谢!

刹国(结束)!!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值