数据结构学习之路--深入探索栈的核心要点(附C源码)

news2024/11/27 18:26:24

   哈喽~大家!今天我们来学习栈的特别节目,精彩马上开始~


目录

前言

一、栈 

1 栈的概念

2 栈的结构

3 栈的实现 

3.1 栈的定义

3.2 栈的初始化 

3.3 入栈 

3.4 出栈 

3.5 取栈顶元素 

 3.6 判断栈是否为空

3.7 栈的大小

3.8 栈的销毁 

 二、源代码


前言

   栈和队列均是常见的数据结构。栈的特点是后进先出(即 Last In First Out(LIFO) ),其增删查元素都在栈顶实现。而队列的特点是先进先出,增加元素在队尾实现,删除和查看元素都在队首实现。本期我们来详细学习实现栈的结构。

一、栈 

我们先根据下图直观地了解栈:

1 栈的概念

   栈是一种特殊的线性表,特点是后进先出,它仅允许在固定一端进行插入和删除元素操作,最先加入的元素最后取出,最后加入的元素最先取出。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。文字描述难免过于死板,为了更好的帮助大家理解,附以下图解: 

将数字 1 到 7 依次入栈之后,此时栈顶元素是 7 ,第一个出栈的元素是 7。

2 栈的结构

3 栈的实现 

   栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。

一般栈的实现需要完成几种函数的操作: 

  • 栈的初始化
  • 入栈
  • 出栈
  • 取出栈顶元素
  • 判断栈是否为空
  • 栈的销毁 
//初始化
void StackInit(ST* ps);
//入栈
void StackPush(ST* ps, STDateType x);
//出栈
void StackPop(ST* ps);
//取栈顶元素
STDateType GetTop(ST* ps);
//判空
bool StackEmpty(ST* ps);
//栈大小
int StackSize(ST* ps)
//栈的销毁
void StackDestory(ST* ps);

3.1 栈的定义

//这里我们实现的是动态的栈

typedef int STDateType;    //方便数据类型的替换

typedef struct Stack        
{
	STDateType* a;         //动态开辟数组
	int top;               //栈顶
	int capacity;          //容量,方便扩容
}ST;

首先,定义动态数组 a,采用动态数组的方式主要是便于后期容量的扩充;然后,定义一个变量 top 用于标识栈顶位置;最后,定义一个变量 capacity 用于统计栈可以容纳的数据个数。 

3.2 栈的初始化 

//初始化
void StackInit(ST* ps)
{
	//判空
	assert(ps);

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

首先,将指向栈中数据内存空间的指针 a 初始化为NULL;然后,将指向栈顶元素的变量 top初始化为0;最后,再将标识栈当前容量的 capacity 初始化为0即可。 

3.3 入栈 

//入栈
void StackPush(ST* ps, STDataType x)
{
	//判空
	assert(ps);
 
	//判断容量是否已满
	//top标识的是最后一个数据的下一个位置,如果想要指向最后一个数据,初始时top=-1
	if (ps->top == ps->capacity)
	{
        //为空就开辟四个空间,不为空,就扩容至二倍
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;

		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);
 
		if (tmp == NULL)
		{
			printf("malloc fail\n");
			exit(-1);
		}
 
		//将新开辟的内存空间的首地址tmp赋值给a
		ps->a = tmp;
		//更新capacity
		ps->capacity = newCapacity;
	}
 
    //入栈
	ps->a[ps->top] = x;
	//top指向栈顶元素的下一个位置
	ps->top++;
}

在入栈之前,首先需要对顺序栈的空间容量进行检查,这里使用三目运算符进行判断:若当前容量 capacity 为空,则开辟4个数据的内存空间;若当前容量非空但已满,则将 capacity 的大小扩容至 2*capacity。然后调用 realloc 函数开辟新的内存空间,并返回新的内存空间的起始地址 tmp。接着将新开辟的内存空间的起始地址 tmp 赋值给 a,使 a 指向这片新开辟的空间。最后,将 x 插入栈顶位置,并让 top 继续指向栈顶元素的下一个位置。 

3.4 出栈 

//出栈
void StackPop(ST* ps)
{
	//判空
	assert(ps);
 
	//判断栈是否为空
	assert(!StackEmpty(ps));
	
	//出栈
	ps->top--;
}

在出栈之前,首先需要调用 StackEmpty(ps) 函数来判断栈是否为空,若不为空,则可以出栈。出栈操作就是将 top--,这里需要注意的是:出栈的数据还残留在内存中,只是逻辑上被删除了。 

3.5 取栈顶元素 

//取栈顶元素
STDataType StackTop(ST* ps)
{
	//判空
	assert(ps);
 
	//判断栈是否为空
	assert(!StackEmpty(ps));
 
	//取栈顶元素
	return ps->a[ps->top - 1];
}

在取栈顶元素之前,首先需要调用函数 StackEmpty(ps) 判断栈是否为空,若栈不为空则可以取栈顶元素。因为 top 指向栈顶元素的下一个位置,所以 top 需要先进行--,再取栈顶元素。 

 3.6 判断栈是否为空

//判空
bool StackEmpty(ST* ps)
{
	assert(ps);

	return ps->top==0;      //返回栈的top,若为0则为空,非0则不为空;
}

判断一个栈是否为空,若为空则返回 true,否则返回 false。这里需要特别说明的一点是,我们将 top 指向栈顶元素的下一个位置,而非指向栈顶元素本身。 

 注意:

  • 当top初始化为:top=0;此时top指向栈顶元素的下一个位置;
  • 当top初始化为:top=-1;此时top指向栈顶元素。

3.7 栈的大小

//大小
int StackSize(ST* ps)
{
	//判空
	assert(ps);
 
	return ps->top;
}

因为 top 指向栈顶元素的下一个位置,而 top 的下标又是从0开始的,所以 top 所在位置的下标就是所求栈的大小,也就是栈中元素个数。 

3.8 栈的销毁 

//销毁
void StackDestory(ST* ps)
{
	//判空
	assert(ps);
 
	//realloc开辟的空间,需要调用free释放
	free(ps->a);
	ps->a = NULL;
 
	ps->top = ps->capacity = 0;
}

对于栈的销毁,首先需要释放由 realloc 动态申请开辟的内存空间,使用 free 函数进行释放。然后将 top 和 capacity 初始化为0。 

 二、源代码

Stack.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
 
​
//这里我们实现的是动态的栈

typedef int STDateType;    //方便数据类型的替换

typedef struct Stack        
{
	STDateType* a;         //动态开辟数组
	int top;               //栈顶
	int capacity;          //容量,方便扩容
}ST;

 
//初始化
void StackInit(ST* ps);
 
//入栈
void StackPush(ST* ps, STDataType x);

//出栈
void StackPop(ST* ps);

//取栈顶元素
STDataType StackTop(ST* ps);

//判空
bool StackEmpty(ST* ps);

//大小
int StackSize(ST* ps);
 
//销毁
void StackDestory(ST* ps);
Stack.c

#include"Stack.h"

​//初始化
void StackInit(ST* ps)
{
	//判空
	assert(ps);

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

​​//入栈
void StackPush(ST* ps, STDataType x)
{
	//判空
	assert(ps);
 
	//判断容量是否已满
	//top标识的是最后一个数据的下一个位置,如果想要指向最后一个数据,初始时top=-1
	if (ps->top == ps->capacity)
	{
        //为空就开辟四个空间,不为空,就扩容至二倍
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;

		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);
 
		if (tmp == NULL)
		{
			printf("malloc fail\n");
			exit(-1);
		}
 
		//将新开辟的内存空间的首地址tmp赋值给a
		ps->a = tmp;
		//更新capacity
		ps->capacity = newCapacity;
	}
 
    //入栈
	ps->a[ps->top] = x;
	//top指向栈顶元素的下一个位置
	ps->top++;
}

​​//出栈
void StackPop(ST* ps)
{
	//判空
	assert(ps);
 
	//判断栈是否为空
	assert(!StackEmpty(ps));
	
	//出栈
	ps->top--;
}

​​//取栈顶元素
STDataType StackTop(ST* ps)
{
	//判空
	assert(ps);
 
	//判断栈是否为空
	assert(!StackEmpty(ps));
 
	//取栈顶元素
	return ps->a[ps->top - 1];
}

​​//判空
bool StackEmpty(ST* ps)
{
	assert(ps);

	return ps->top==0;      //返回栈的top,若为0则为空,非0则不为空;
}

​​//大小
int StackSize(ST* ps)
{
	//判空
	assert(ps);
 
	return ps->top;
}

​​//销毁
void StackDestory(ST* ps)
{
	//判空
	assert(ps);
 
	//realloc开辟的空间,需要调用free释放
	free(ps->a);
	ps->a = NULL;
 
	ps->top = ps->capacity = 0;
}
​
test.c

 
#include"Stack.h"
 
void TestStack()
{
	ST st;
 
	//初始化
	StackInit(&st);
 
	//入栈
	StackPush(&st, 1);
	StackPush(&st, 2);
	StackPush(&st, 3);
	StackPush(&st, 4);
	StackPush(&st, 5);
    StackPush(&st, 6);
    StackPush(&st, 7);

	//出栈
	while (!StackEmpty(&st))
	{
		//读取栈顶元素
		printf("%d ",StackTop(&st));
		
		//出栈
		StackPop(&st);
	}
	printf("\n");
 
	//销毁
	StackDestory(&st);
}
 
int main()
{
	TestStack();
 
	return 0;
}

   本期的内容不多,相信大家学起来也相对容易,提前透露一下下:我们下期来玩队列哈~如果你们觉得本篇文章对自己有所帮助,给博主留下三连支持噢,你的支持是我创作的最大动力!那我们下期再会啦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1599599.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MES系统功能有什么?对企业有什么价值?

制造业为什么上MES&#xff1a; MES最核心的东西是对生产进行管控&#xff0c;实现生产工厂的透明化、实时化、可控化、可追溯化。 上MES的目的&#xff1a; 最为根本的目的是可以对生产当中的质量进行管控&#xff0c;对交期进行管理&#xff0c;对成本进行控制&#xff0c;…

Java SDK 使用示例

我们使用同样的 Maven 模板去创建 use-test-sdk 项目&#xff0c;不同的是&#xff0c;我们需要在 use-tset-sdk 下创建一个 lib 文件夹&#xff0c;用来存放 test-sdk.jar 文件。 我们将 test-sdk.jar 拖放到 use-test-sdk/lib 下&#xff0c;然后我们就可以编写代码了&#…

47.HarmonyOS鸿蒙系统 App(ArkUI)创建轮播效果

创建轮播效果&#xff0c;共3页切换 Entry Component struct Index {State message: string Hello Worldprivate swiperController: SwiperController new SwiperController()build() {Swiper(this.swiperController) {Text("第一页").width(90%).height(100%).bac…

Appium Desktop + Appium Inspector + 模拟器连接

一、环境预备 1.你需要安装好配置好adb,确保可以在命令行直接运行adb指令 2.安装Appium Desktop、Appium Inspector 、 模拟器 二、启动appium 服务 启动后&#xff0c;画面如下&#xff1a; 三、启动模拟器 此时&#xff0c;启动模拟器&#xff0c;打开电脑cmd窗口&#x…

记第一次踩坑Gradle

今天有个项目只能使用Gradle编译&#xff0c;没办法了&#xff0c;尝试吧。 先去下载了最新版本的Gradle&#xff0c;然后配置好了环境变量&#xff0c;可以在命令行使用gradle命令了。 然后打开项目开始操作一番&#xff0c;但是上来就傻眼了。 我白下载了&#xff0c;又重新下…

【中间件】ElasticSearch简介和基本操作

一、简介 Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析引擎&#xff0c;支持各种数据类型&#xff0c;包括文本、数字、地理、结构化、非结构化 ,可以让你存储所有类型的数据&#xff0c;能够解决不断涌现出的各种用例。其构成如下&#xff1a; 说明&#xff1…

AI赋能校园管理,打造平安智慧校园解决方案

背景&#xff1a; 2020年教育部办公厅印发《教育系统安全专项整治三年行动实施方案》&#xff0c;文中要求&#xff0c;学校在所辖范围内组织开展安全专项整治三年行动&#xff0c;健全完善安全责任体系&#xff0c;建立风险管控和隐患治理的安全防控体系&#xff0c;开展消防等…

【Qt 学习笔记】QWidget的windowOpacity属性 | cursor属性 | font属性

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ QWidget的windowOpacity属性 | cursor属性 | font属性 文章编号&#…

模板初阶的学习

目录&#xff1a; 一&#xff1a;泛型模板 二&#xff1a;函数模板 三&#xff1a;类模板 1&#xff1a;泛型模板 泛型编程&#xff1a;编写与类型无关的通用代码&#xff0c;是代码复用的一种手段。模板是泛型编程的基础。 以交换函数为列进行讲解&#xff1a; void Swap(…

RUST腐蚀服务器添加 TAGS标签教程

RUST腐蚀服务器添加 TAGS标签教程 大家好我是艾西&#xff0c;一个做服务器租用的网络架构师。我们自己搭建架设的服务器在steam展示面板看到跟别人的不一样是咋回事&#xff1f; 这个其实就是服务器的一个标签&#xff0c;那么主要的作用就是让大家在选择服务器时更快更直接的…

DePIN打猎之旅:AI算力作饵,道阻且长

出品&#xff5c;OKG Research 作者&#xff5c;Hedy Bi 香港Web3嘉年华已告一段落&#xff0c;然而Web3自由的脉搏还在跳动&#xff0c;并不断向其他行业渗透。和上一轮周期相比&#xff0c;本轮牛市开启的逻辑是由“原生创新叙事”转变成“主流认可&#xff0c;资金驱动”的…

深度学习基础——计算量、参数量和推理时间

深度学习基础——计算量、参数量和推理时间 在深度学习中&#xff0c;计算量、参数量和推理时间是评估模型性能和效率的重要指标。本文将介绍这三个指标的定义、计算方法以及如何使用Python进行实现和可视化展示&#xff0c;以帮助读者更好地理解和评估深度学习模型。 1. 定义…

C++之linux系统开发调试(包括开发插件的安装)

背景&#xff1a;写这篇文章确实是折腾了很久&#xff0c;而且仍然没有搞利索。但是可以进行命令提示 一、安装步骤 1. 使用普通用户进行安装 sudo apt install build-essential cmake vim-nox python3-dev mono-complete golang nodejs default-jdk npm2. 安装vundle git c…

嵌入式学习——C语言基础——day2

1. gcc的编译过程 1.1 预处理 处理和 " # " 相关的代码 gcc -E filename.c -o filename.i 1. 头文件 2. 宏定义 3. 条件编译 1.2 编译 将c语言代码编译成汇编代码 gcc -S filename.c -o filename.s 1.3 汇编 将汇编代码编译成二进制代码 gcc -c filename.c -o f…

研发岗-统信UOS系统配置npm git等前端常用配置

第一步 获取root权限 配置环境等都需要用到root权限&#xff0c;所以我们先获取到root权限&#xff0c;方便下面的操作 下载软件 在UOS应用商店下载的所需应用 版本都比较低 安装node 官网下载了【arm64】的包&#xff0c;解压到指定文件夹&#xff0c;设置链接&#xff0…

MacOs 安装thrift-0.5.0

下载thrift-0.5.0.tar.gz https://archive.apache.org/dist/incubator/thrift/0.5.0-incubating/ 安装thrift 解压&#xff1a;tar -zvxf thrift-0.5.0.tar.gz 进入解压目录&#xff1a;cd thrift-0.5.0 编译命令&#xff1a;./configure --prefix/usr/local/ --with-boo…

加强金融行业关键信息基础设施安全保护,有效防范网络安全风险

当前&#xff0c;随着数字化发展的不断深入&#xff0c;关键信息基础设施作为国家的重要战略资源&#xff0c;面临着国内外严峻的网络安全风险。为了确保国家安全&#xff0c;在国家发展各领域和全过程中&#xff0c;需要将安全发展贯穿始终&#xff0c;筑牢国家安全屏障。金融…

Python 入门指南(四)

原文&#xff1a;zh.annas-archive.org/md5/97bc15629f1b51a0671040c56db61b92 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第十章&#xff1a;哈希和符号表 我们之前看过列表&#xff0c;其中项目按顺序存储并通过索引号访问。索引号对计算机来说很有效。它们是整…

香港开启虚拟资产ETF通道:5亿美元规模预期是保守还是乐观?

出品&#xff5c;OKG Research 作者&#xff5c;Hedy Bi 比特币现货ETF获批&#xff0c;已不是新鲜事。据路透社昨日消息&#xff0c;至少三家离岸中国资产管理公司将很快推出香港虚拟资产现货ETF&#xff08;比特币现货和以太坊现货ETF&#xff09;。香港政府对Web3的大力支…

Docker+Uwsgi部署Django项目

在之前的文章中&#xff0c;已经给大家分享了在docker中使用django自带的命令部署项目&#xff0c;这篇文章主要讲解如何使用uwsgi部署。 1. 在Django项目的根目录下新建Dockerfile文件 #Dockerfile文件 # 使用 Python 3.9 作为基础镜像 FROM python:3.9# 设置工作目录 WORKDI…