数据结构——栈的基本操作

news2024/10/1 14:04:32

前言

介绍

🍃数据结构专区:数据结构

参考

该部分知识参考于《数据结构(C语言版 第2版)》55 ~ 59页

🌈每一个清晨,都是世界对你说的最温柔的早安:ૢ(≧▽≦)و✨


1、栈的基本概念

栈(Stack)是一种遵循后进先出(LIFO, Last In First Out)原则的有序集合。

  • 数组实现:使用数组的一个连续空间来存储栈中的元素,通常使用一个指针(或索引)来指示栈顶的位置。数组实现的栈在添加和删除元素时,如果数组已满或为空,可能需要进行扩容或缩容操作,这可能会涉及到额外的性能开销。
  • 链表实现:使用链表的头部(或尾部,取决于具体实现)作为栈顶,通过修改链表的头指针(或尾指针)来实现元素的添加和删除。链表实现的栈在添加和删除元素时,不需要进行扩容或缩容操作,因此通常具有更好的性能。

2、数组栈的实现

2.1  宏定义

#include<iostream>
using namespace std;
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status;

2.2  数组栈的结构体定义

#define MAXSIZE 100  //顺序表的存储范围
#define STACK_INCREMENT 2  //存储空间分配增量

//这里假定SElemType是int类型
typedef int SElemType;

typedef struct
{
	SElemType* base;   //栈底指针
	SElemType* top;    //栈顶指针
	int stacksize;     //栈可用的最大容量
}SqStack;

2.3  初始化数组栈

//初始化栈
Status InitStack(SqStack& S)
{
	S.base = new SElemType[MAXSIZE];
	if (!S.base)
		exit(OVERFLOW);
	S.top = S.base;  //top初始化为base,空栈
	S.stacksize = MAXSIZE;  //stacksize的最大容量为MAXSIZE
	return OK;
}

 2.4  销毁数组栈

//销毁栈
Status DestroyStack(SqStack& S)
{
	free(S.base);
	S.base = NULL;
	S.top = NULL;  //规范指针
	S.stacksize = 0;
	return OK;
}

2.5  清空数组栈

//清除栈
Status CleanStack(SqStack& S)
{
	S.top = S.base; //让top指针指回栈底即可
	return OK;
}

2.6  判空

//判空
Status StackEmpty(SqStack S)
{
	if (S.top == S.base) //如果栈顶和栈底指向同一个位置则证明栈为NULL
		return OK;
	else
		return ERROR;
}

2.7  获取栈内元素个数

//获取栈内元素数量
int StackLength(SqStack S)
{
	return S.top - S.base;
}

2.8  获取栈顶元素

//获取栈顶元素
SElemType GetTop(SqStack S)
{
	//判断栈是否为空
	if (!StackEmpty(S))//不为空即可获取元素
	{
		return *(--S.top);
	}
	else
	{
		return ERROR;
	}
}

2.9  入栈

//入栈
Status Push(SqStack& S, SElemType e)
{
	//插入元素为新的栈顶元素
	if (S.top - S.base == S.stacksize)//满栈
	{
		S.base = (SElemType*)realloc(S.base, (S.stacksize + STACK_INCREMENT) * sizeof(SElemType));
		//判断是否扩容成功
		if (!S.base)
			exit(OVERFLOW);//如果扩容失败,退出程序
		S.top = S.base + S.stacksize;
	}
	*(S.top)++ = e;  //这里是先对S.top解引用后存入数据e,随后将top指针向后移动一位
}

2.10  出栈

//出栈
Status Pop(SqStack& S, SElemType &e)
{
	//删除栈顶元素,并返回其值
	if (!StackEmpty(S)) //栈不为空进行操作
	{
		e = *(--S.top);
		return OK;
	}
	else
		return ERROR;
}

2.11  visit()函数

// 定义一个函数visit,用于打印元素
void visit(SElemType e)
{
	std::cout << e << " ";
}

2.12  遍历数组栈

// 定义一个函数用于遍历栈中的元素并对每个元素执行visit函数
void StackTraverse(SqStack S, void(*visit)(SElemType)) 
{
	SElemType* p = S.base;
	while (S.top > p) //p指向栈元素
		visit(*p++); //对该栈调用visit(),p指针上移一个存储单元
	printf("\n");
}

2.13  整体代码(含测试代码)

#include<iostream>
using namespace std;
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status;

#define MAXSIZE 100  //顺序表的存储范围
#define STACK_INCREMENT 2  //存储空间分配增量

//这里假定SElemType是int类型
typedef int SElemType;

typedef struct
{
	SElemType* base;   //栈底指针
	SElemType* top;    //栈顶指针
	int stacksize;     //栈可用的最大容量
}SqStack;

//SqStack S;  //声明该栈

//初始化栈
Status InitStack(SqStack& S)
{
	S.base = new SElemType[MAXSIZE];
	if (!S.base)
		exit(OVERFLOW);
	S.top = S.base;  //top初始化为base,空栈
	S.stacksize = MAXSIZE;  //stacksize的最大容量为MAXSIZE
	return OK;
}

//销毁栈
Status DestroyStack(SqStack& S)
{
	free(S.base);
	S.base = NULL;
	S.top = NULL;  //规范指针
	S.stacksize = 0;
	return OK;
}

//清除栈
Status CleanStack(SqStack& S)
{
	S.top = S.base; //让top指针指回栈底即可
	return OK;
}

//判空
Status StackEmpty(SqStack S)
{
	if (S.top == S.base) //如果栈顶和栈底指向同一个位置则证明栈为NULL
		return OK;
	else
		return ERROR;
}

//获取栈内元素数量
int StackLength(SqStack S)
{
	return S.top - S.base;
}

//获取栈顶元素
SElemType GetTop(SqStack S)
{
	//判断栈是否为空
	if (!StackEmpty(S))//不为空即可获取元素
	{
		return *(--S.top);
	}
	else
	{
		return ERROR;
	}
}

//入栈
Status Push(SqStack& S, SElemType e)
{
	//插入元素为新的栈顶元素
	if (S.top - S.base == S.stacksize)//满栈
	{
		S.base = (SElemType*)realloc(S.base, (S.stacksize + STACK_INCREMENT) * sizeof(SElemType));
		//判断是否扩容成功
		if (!S.base)
			exit(OVERFLOW);//如果扩容失败,退出程序
		S.top = S.base + S.stacksize;
	}
	*(S.top)++ = e;  //这里是先对S.top解引用后存入数据e,随后将top指针向后移动一位
}

//出栈
Status Pop(SqStack& S, SElemType &e)
{
	//删除栈顶元素,并返回其值
	if (!StackEmpty(S)) //栈不为空进行操作
	{
		e = *(--S.top);
		return OK;
	}
	else
		return ERROR;
}

// 定义一个函数visit,用于打印元素
void visit(SElemType e)
{
	std::cout << e << " ";
}

// 定义一个函数用于遍历栈中的元素并对每个元素执行visit函数
void StackTraverse(SqStack S, void(*visit)(SElemType)) 
{
	SElemType* p = S.base;
	while (S.top > p) //p指向栈元素
		visit(*p++); //对该栈调用visit(),p指针上移一个存储单元
	printf("\n");
}

int main() {
	int j;
	SqStack s;
	SElemType e;
	InitStack(s);
	for (j = 1; j <= 12; j++)
		Push(s, j);
	printf("栈中元素依次为\n");
	StackTraverse(s, visit);
	Pop(s, e);
	printf("弹出的栈顶元素e = %d\n", e);
	printf("栈空否? %d (1:空 0:否)\n", StackEmpty(s));
	e = GetTop(s);
	printf("栈顶元素e = %d,栈的长度为%d\n", e, StackLength(s));
	CleanStack(s);
	printf("清空栈后,栈空否? %d (1:空 0:否)\n", StackEmpty(s));
	DestroyStack(s);
	printf("销毁栈后,s.top = %u,s.base = %u,s.stacksize = %d\n", s.top, s.base, s.stacksize);
}

3、链表栈的实现

3.1  宏定义

#include<iostream>
using namespace std;
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status;

3.2  链表栈的结构体定义

typedef int SElemType;
typedef struct StackNode
{
	SElemType data;
	struct StackNode* next;
}StackNode, *LinkStack;

3.3  初始化链表栈

//初始化
Status InitStack(LinkStack& S)
{
	//让栈顶指针指向NULL即可
	S = NULL;
	return OK;
}

3.4  清空栈

//清空栈
Status ClearStack(LinkStack& S)
{
	//创建一个临时指针,遍历该链表后依次释放各个节点
	StackNode* p;
	while (S)
	{
		p = S;
		S = S->next;
		delete p;
	}
	return OK;
}

3.5  判空

//判空
Status StackEmpty(LinkStack S)
{
	return S == NULL;
}

3.6  销毁链表栈

//销毁
Status DestroyStack(LinkStack& S)
{
	ClearStack(S);
	S = NULL;
	return OK;
}

3.7  入栈

//入栈
Status Push(LinkStack& S, SElemType e)
{
	//在栈顶位置插入元素e
	StackNode* p = new StackNode;
	p->data = e;
	p->next = S;  //将新结点插入栈顶
	S = p;        //修改栈顶指针为p
	return OK;
}

3.8  出栈

//出栈
Status Pop(LinkStack &S, SElemType& e)
{
	//删除栈顶元素,并返回该元素
	if (S == NULL)
		return ERROR;
	StackNode * p = S;
	e = p->data;
	S = S->next;
	delete p;
	return OK;
}

 3.9  获取栈顶元素

//获取栈顶元素
SElemType GetTop(LinkStack S)
{
	//返回S的栈顶元素,并不改变栈顶指针的位置
	if (S != NULL)
	{
		return S->data;
	}
}

3.10  遍历打印

// 遍历栈并打印
void StackTraverse(LinkStack S) 
{
	StackNode* p = S;
	while (p) 
	{
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}

3.11  整体代码(含测试代码)

#include<iostream>
using namespace std;
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status;

typedef int SElemType;
typedef struct StackNode
{
	SElemType data;
	struct StackNode* next;
}StackNode, *LinkStack;

//初始化
Status InitStack(LinkStack& S)
{
	//让栈顶指针指向NULL即可
	S = NULL;
	return OK;
}

//清空栈
Status ClearStack(LinkStack& S)
{
	//创建一个临时指针,遍历该链表后依次释放各个节点
	StackNode* p;
	while (S)
	{
		p = S;
		S = S->next;
		delete p;
	}
	return OK;
}


//判空
Status StackEmpty(LinkStack S)
{
	return S == NULL;
}

//销毁
Status DestroyStack(LinkStack& S)
{
	ClearStack(S);
	S = NULL;
	return OK;
}

//入栈
Status Push(LinkStack& S, SElemType e)
{
	//在栈顶位置插入元素e
	StackNode* p = new StackNode;
	p->data = e;
	p->next = S;  //将新结点插入栈顶
	S = p;        //修改栈顶指针为p
	return OK;
}

//出栈
Status Pop(LinkStack &S, SElemType& e)
{
	//删除栈顶元素,并返回该元素
	if (S == NULL)
		return ERROR;
	StackNode * p = S;
	e = p->data;
	S = S->next;
	delete p;
	return OK;
}

//获取栈顶元素
SElemType GetTop(LinkStack S)
{
	//返回S的栈顶元素,并不改变栈顶指针的位置
	if (S != NULL)
	{
		return S->data;
	}
}

// 遍历栈并打印
void StackTraverse(LinkStack S) 
{
	StackNode* p = S;
	while (p) 
	{
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}

int main()
{

	LinkStack S;
	InitStack(S);
	int e;
	Push(S, 1);
	Push(S, 2);
	Push(S, 3);
	printf("现在栈内元素为(后进先出):");
	StackTraverse(S);
	printf("栈顶元素为:%d\n", GetTop(S));
	Pop(S, e);
	printf("现在栈内元素为(后进先出):");
	StackTraverse(S);
	printf("弹出一个元素后,栈顶元素为:%d\n", GetTop(S));
	ClearStack(S);
	if (StackEmpty(S)) 
	{
		printf("清空栈后,栈为空\n");
	}
	else 
	{
		printf("清空栈后,栈不为空,证明有问题\n");
	}
	DestroyStack(S);
	return 0;
}

结语

到此我们的两种栈的实现代码就完成了,我们可以发现,在实现栈的过程中远没有当初学习顺序表那么困难,那是因为栈其实就是一种特殊结构的顺序表,并且在前面的学习顺序表过程中,我故意将ElemType写为一种结构体类型,让我们在学习顺序表时写起来就有些困难,在前面学会之后看到这里就游刃有余了!

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

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

相关文章

LabVIEW提高开发效率技巧----RT与FPGA模块

LabVIEW RT&#xff08;Real-Time&#xff09;和FPGA模块是为开发实时系统和高性能控制应用而设计的&#xff0c;能够有效满足工业控制、自动化测试、信号处理等领域的严格要求。通过这两个模块&#xff0c;开发者可以充分发挥LabVIEW的并行处理能力&#xff0c;实现高效稳定的…

L0-Linux-关卡材料提交

SSH全称Secure Shell&#xff0c;中文翻译为安全外壳&#xff0c;它是一种网络安全协议&#xff0c;通过加密和认证机制实现安全的访问和文件传输等业务。SSH 协议通过对网络数据进行加密和验证&#xff0c;在不安全的网络环境中提供了安全的网络服务。 SSH 是&#xff08;C/S…

基于SpringBoot+Vue的学生宿舍管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

中国雕塑——孙溟㠭浅析《瘗鹤铭》

中国雕塑——孙溟㠭浅析碑帖《瘗鹤铭》 《瘞鹤铭》 《瘗鹤铭》是原刻于镇江焦山西麓崖壁上的摩崖石刻&#xff0c;属楷书体。是一位隐士为一只死去的鹤所撰的纪念文字。时代和书写者众说纷纭&#xff0c;前人有说晋王羲之&#xff0c;有说梁代陶弘景&#xff0c;有人认为是隋朝…

linux文件编程_文件

1. 文件编程概述 之前在windows中对文件的操作是&#xff1a;打开文档—>编辑文档—>保存文档—>关闭文档 我们的Linux文件编程主要是利用代码对文件进行操作&#xff1a;文件创建、打开、编辑等自动化执行等 在Linux我们要使用编程调用api函数的方式进行文档的编辑…

怎么将电脑的“任务栏”设置为“透明”状态?任务栏透明度设置攻略!扩散!

怎么将电脑的“任务栏”设置为“透明”状态? ■ 在Windows系统中&#xff0c;电脑任务栏设置为透明状态的方式有很多种&#xff0c;可以通过电脑自带的个性化设置&#xff0c;或者注册表编辑&#xff0c;还可以通过第三方软件进行任务栏丰富的修改。 在Windows操作系统中&…

【UEFI基础】BIOS下的NVMe

什么是NVMe NVMe全称NonVolatile Memory Express&#xff08;非易失性内存主机控制器接口规范&#xff09;&#xff0c;其官方&#xff08;NVMe官网NVM Express&#xff09;定义将其描述为“一个开放的标准和信息集合&#xff0c;以充分释放非易失性存储在从移动端到数据中心的…

【YOLO系列】YOLOv11正式发布!

Yolov11发布文档 代码链接 了解Ultralytics YOLO11的所有突破性功能&#xff0c;这是我们最新的人工智能模型&#xff0c;具有无与伦比的准确性和效率。 我们很高兴向大家介绍Ultralytics型号的下一次进化&#xff1a;YOLO11&#xff01;YOLO11建立在以前YOLO模型版本令人印象…

【易上手快捷开发新框架技术】用Flet从零开始分步骤循序渐进编程实现购物清单助手手机应用app示例掰烂嚼碎深度讲解源代码IDE运行和调试通过截图为证

传奇开心果编程实例微博文 序言首先&#xff0c;明确任务&#xff0c;任务驱动&#xff1a;其次&#xff0c;开发工具选型考虑&#xff1a;第三&#xff0c;编程思路和应用结构设计&#xff1a; 第一步&#xff1a;从零开始搭建移动应用雏形框架第二步&#xff1a;设置窗口大小…

PCL 移除点云边缘不连续的点

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 法向量计算 2.1.2 边界检测和移除 2.1.3 边界检测和移除 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&…

React学习笔记(4.0)

json-server实现数据Mock 1.项目中安装json-server npm i -D json-server 2.准备一个json文件 3.添加启动命令【package.json中配置】 "server":"json-server ./server/data.json --port 8888" 该命令中&#xff0c;路径就是自己创建的json文件路径&…

【C++】BitSet和Bloom_Filter

前言&#xff1a; 在计算机图形学中&#xff0c;位图&#xff08;Bitmap&#xff09;也称为光栅图&#xff0c;是由像素点组成的图像表示方式。在 C 编程中&#xff0c;位图可以通过特定的函数和数据结构来进行处理和操作。 BitMap 位图&#xff08;BitMap&#xff09;是一种数…

uniapp中uni.request的统一封装 (ts版)

文章目录 前言一、我们为什么要去封装&#xff1f;二、具体实现1.创建一个请求封装文件&#xff1a;2.封装 uni.request&#xff1a;3.如何去使用&#xff1f; 总结 前言 在uniapp中如何去更简洁高效的发送我们的请求&#xff0c;下面就介绍了uni.request()二次封装。 一、我们…

超强大的 Nginx 可视化管理工具

今天给大家介绍一款 Nginx 可视化管理界面&#xff0c;非常好用&#xff0c;小白也能立马上手。 nginx-proxy-manager 是一个反向代理管理系统&#xff0c;它基于 NGINX&#xff0c;具有漂亮干净的 Web UI。还可以获得受信任的 SSL 证书&#xff0c;并通过单独的配置、自定义和…

【计算机视觉】ch1-Introduction

相机模型与成像 1. 世界坐标系 (World Coordinate System) 世界坐标系是指物体在真实世界中的位置和方向的表示方式。在计算机视觉和图像处理领域&#xff0c;世界坐标系通常是一个全局坐标系统&#xff0c;描述了摄像机拍摄到的物体在实际三维空间中的位置。它是所有其他坐标…

【嵌入式开发】可编程4k蓝牙摄像头点击器

今天讲解的是一款可编程摄像头蓝牙点击器&#xff0c;支持离线文字识别 外观如下 B站有使用视频 链接: B站使用 下面是具体使用步骤&#xff08;主要根据B站视频使用流程总结&#xff09;&#xff0c;文末附源码 全是干货 执行main代码 代码运行后&#xff0c;检查摄像头…

【电机-概述及分类】

文章目录 第1章1-1 电机的定义1-2 电机的构成要素1-3 电机的分类1-3-1 直流电机1-3-1-1 永磁励磁型直流电机1-3-1-2 电磁铁励磁型直流电机 第1章 重新认识电机的体系 电机包括许多种类。换个角度来看&#xff0c;并没有完美的电机&#xff0c;某种电机具有所谓A的优点&#xf…

docker 搭建minimalist-web-notepad

亲测可用 服务器 ubuntu sudo mkdir -p /root/docker/note# 切换到rootsu -chmod -R 755 /root/docker/notedocker pull carlosa/minimalist-web-notepad# 容器端口80 映射到 服务器7001端口&#xff0c;记得开防火墙 docker run -d --name notepad --restart always -p 7001…

u盘格式化后数据能恢复吗?2024年Top4恢复神器来帮忙

在这个电脑和手机满天飞的时代&#xff0c;U盘是我们用来存东西和传文件的得力助手&#xff0c;特别重要。但是&#xff0c;有时候U盘可能会不小心被格式化了&#xff0c;里面的重要文件就不见了。那么&#xff0c;U盘格式化后的数据还能恢复吗&#xff1f;当然可以。今天会告诉…

揭秘一下平时我们下载的python库跑到哪里了呢???

&#xff08;阅读之前&#xff0c;祝福大家国庆假期快乐&#xff0c;以及真诚的祝福我们的祖国越来越强大&#xff09;在那天的课上&#xff0c;老师问了我们这样一个问题&#xff1a;你们知道你们平时pip install下载库&#xff0c;下载好了&#xff0c;你们的库是下载到哪里了…