【深入解析:数据结构栈的魅力与应用】

本章重点

  • 栈的概念及结构

  • 栈的实现方式

  • 数组实现栈接口

  • 栈面试题目

  • 概念选择题

一、栈的概念及结构

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

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶

出栈:栈的删除操作叫做出栈。出数据也在栈顶

  • 栈顶Top:线性表允许插入和删除的那一端。
  • 栈底Bottom:固定的,不允许进行插入和删除的另一端。
  • 空栈:不含任何元素的空表。

二、栈的实现方式

        由于栈只能在栈顶操作,所以栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。单链表的尾插需要遍历结点,这里使用带头双向循环链表也可,但是空间销毁大。单链表也可以实现,我们就需要让栈顶是头结点,栈底是尾结点,这样入栈就是头插,效率高。

1.顺序堆栈


        根据前边的分析可知,顺序堆栈和顺序表的数据成员(元素)是相同的,不同之处是,顺序堆栈的入栈和出栈操作只能对当前栈顶元素进行。
        顺序堆栈的存储结构如图3-2所示。其中,a0,a1,a2,表示顺序堆栈要存储的元素序列,stack 表示顺序堆栈存放元素的数组,capacity 表示顺序堆栈数组stack的内存单元容量(表示目前允许存储的元素最大个数),top表示顺序堆栈数组stack的当前栈顶位置。

2.链式堆栈

        堆栈有两端,插入元素和删除元素的一端为栈顶,另一端为栈底。对链式堆栈来说,显然,若把靠近头指针的一端定义为栈顶,则插入元素和删除元素时不需要遍历整个链,其时间复杂度为0(1);若把远离头指针的一端定义为栈顶,则每次插入元素和删除元素时都需要遍历整个链,其时间复杂度为0(n)。因此,链式堆栈都设计成把靠近头指针的一端定义为栈顶。链式堆栈的头结点对操作实现的影响不大,因此可有可无。依次向带头结点链式堆栈输入a0,a1,a2,…….an-1后,带头结点链式堆栈的结构示意图如图3-3所示。

三、数组实现栈接口

// 下面是定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈
typedef int STDataType;
#define N 10
typedef struct Stack
{
	STDataType a[N];
	int top; // 栈顶
}Stack;
// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top; // 栈顶
	int capacity; // 容量
}Stack;

// 初始化栈
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.初始化栈:void StackInit(Stack* ps)

初始化这里我们先不设置容量,后续入栈再申请空间即可,这里需要注意top的值,我们设置的值是0,它是表示非空栈中的栈顶指针始终在栈顶元素的下一个位置上。

// 初始化栈
void StackInit(Stack* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0;//表示栈顶元素的下一个位置
	ps->capacity = 0;
}

2.入栈:void StackPush(Stack* ps, STDataType data)

满栈:当我们使一个元素入栈的之前,我们往往需要判断一下栈是否为满栈,防止发生上溢的情况。因为我们定义了一个capacity来表示当前已经分配的存储空间,所以我们可以用ps->top == ps->capacity 来判断当前使用的栈空间是否满了。所以当ps->top == ps->capacity时表示已经满了。

满栈我们要首先追加存储空间,然后才能将元素入栈。realloc()函数可以申请空间,如果realloc第一个参数为空,那么realloc的功能就类似于malloc,这也是为什么我们前面初始化没有开辟空间的原因,写在这里能省大量的代码。

// 入栈
void StackPush(Stack* ps, STDataType data)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		int newCapacity = (ps->capacity == 0) ? 4 : ps->capacity * 2;
		//如果ps->a == NULL,功能相当与malloc
		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newCapacity;
	}
	ps->a[ps->top] = data;
	ps->top++;
}

3.出栈:void StackPop(Stack* ps)

出栈时我们首先要判断栈是否为空栈,由于是顺序栈,我们只需要判断ps->top > 0即可判断当前栈的情况。

// 出栈
void StackPop(Stack* ps)
{
	assert(ps);
	//保证不为空
	assert(ps->top > 0);
	ps->top--;
}

4.获取栈顶元素:STDataType StackTop(Stack* ps)

取栈顶元素同样需要判断是否为空栈,空栈也不能取出任何数据,所以这里强制断言,一旦为空栈直接程序报错

// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
	assert(ps);
	//保证不为空
	assert(ps->top > 0);
	return ps->a[ps->top - 1];
}

5.获取栈中有效元素个数:int StackSize(Stack* ps)

由于top是非空栈中的栈顶指针始终在栈顶元素的下一个位置上,所以top就是当前栈中有效元素个数

// 获取栈中有效元素个数
int StackSize(Stack* ps)
{
	assert(ps);

	return ps->top;
}

6.检测栈是否为空,如果为空返回0,如果不为空返回非零结果:int StackEmpty(Stack* ps)

// 检测栈是否为空,如果为空返回0,如果不为空返回非零结果
int StackEmpty(Stack* ps)
{
	if (ps->top != 0)
		return ps->top;
	else
		return 0;
}

7.销毁栈:void StackDestroy(Stack* ps)

销毁栈只需将申请的空间释放,再把设置的大小和容量设施为0即可。

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

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

四、栈面试题目

括号匹配问题。OJ链接

        当开始接触题目时,我们会不禁想到如果计算出左括号的数量,和右括号的数量,如果每种括号左右数量相同,会不会就是有效的括号了呢?

        事实上不是的,假如输入是 [ { ] },每种括号的左右数量分别相等,但不是有效的括号。这是因为结果还与括号的位置有关。

        仔细分析我们发现,对于有效的括号,它的部分子表达式仍然是有效的括号,比如 { ( )[ ) ] } 是一个有效的括号,( )[ { } ] 是有效的括号,[ ( ) ] 也是有效的括号。并且当我们每次删除一个最小的括号对时,我们会逐渐将括号删除完。比如下面的例子。

这个思考的过程其实就是栈的实现过程。因此我们考虑使用栈,当遇到匹配的最小括号对时,我们将这对括号从栈中删除(即出栈),如果最后栈为空,那么它是有效的括号,反之不是。

bool isValid(char* s) 
{
	Stack st;
	StackInit(&st);
	char topVal;
	while (*s)
	{
		switch (*s)
		{
		case'(':
		case'[':
		case'{':
			StackPush(&st, *s);
			break;
		case')':
		case']':
		case'}':
			//数量不匹配
			if (!StackEmpty(&st))//栈为空
			{
				StackDestroy(&st);//防止内存泄露
				return false;
			}
			topVal = StackTop(&st);
			StackPop(&st);
			//顺序不匹配
			if (*s == ')' && topVal != '(' 
				|| *s == ']' && topVal != '['
				|| *s == '}' && topVal != '}')
			{
				StackDestroy(&st);//防止内存泄露
				return false;
			}
			break;			
		}
		++s;
	}
	//栈不为空,此时只有右括号,false,说明数量不匹配
	int ret = StackEmpty(&st);
	StackDestroy(&st);
	if (ret == 0)
		return false;
	else
		return true;
}

五、概念选择题

1.一个栈的初始状态为空。现将元素1、2、3、4、5、A、B、C、D、E依次入栈,然后再依次出栈,则元素出栈的顺序是( )。

  • A 、12345ABCDE
  • B 、EDCBA54321
  • C 、ABCDE12345
  • D 、54321EDCBA

元素出栈的顺序遵循后进先出(Last-In-First-Out,LIFO)原则,因此选择B

2.若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是()

  • A 1,4,3,2
  • B 2,3,4,1
  • C 3,1,4,2
  • D 3,4,2,1

A:1入栈后出栈,2,3,4入栈,4出栈,3出栈,2出栈,可能。

B:1,2入栈,2出栈,3入栈,3出栈,4入栈,4出栈,1出栈,可能。

C:1,2,3入栈,3出栈,接下来应该是2出栈或者4入栈,不可能1出栈,不可能。

D:1,2,3入栈,3出栈,4入栈,4出栈,2出栈,1出栈,可能。

本章结束啦!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Masterstudy主题 - 用于线上教育、在线学习和在线课程的LMS WordPress主题

Masterstudy主题是每个人的最佳选择!它是一个完整的线上教育WordPress主题,适合所有想要创建在线课程、辅导和语言中心、在线学习平台并在全球范围内传播知识的人。这是一个完美的教育主题,旨在满足学习行业的需求。 网址:Master…

【数据结构练习】单链表OJ题(一)

目录 一、移除链表元素思路1:思路2: 二、反转链表三、链表的中间节点四、链表中倒数第k个节点五、回文结构六、合并两个有序链表 一、移除链表元素 题目: 思路1: 在原来的链表上进行修改,节点的数据是val的删除&am…

axios 各种方式的请求 示例

GET请求 示例一&#xff1a; 服务端代码 GetMapping("/f11") public String f11(Integer pageNum, Integer pageSize) {return pageNum " : " pageSize; }前端代码 <template><div class"home"><button click"getFun1…

时序预测 | MATLAB实现ELM极限学习机时间序列预测(多指标、相关图)

时序预测 | MATLAB实现ELM极限学习机时间序列预测(多指标、相关图) 目录 时序预测 | MATLAB实现ELM极限学习机时间序列预测(多指标、相关图)效果一览基本介绍程序设计学习总结参考资料效果一览 基本介绍 时序预测 | MATLAB实现ELM极

linux部署clickhouse(单机)

一、下载安装 1.1、下载地址 阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区阿里巴巴开源镜像站&#xff0c;免费提供Linux镜像下载服务&#xff0c;拥有Ubuntu、CentOS、Deepin、MongoDB、Apache、Maven、Composer等多种开源软件镜像源&#xff0c;此外还提供域名解析DNS、…

Android企业项目开发实训室建设方案

一 、系统概述 Android企业项目开发作为新一代信息技术的重点和促进信息消费的核心产业&#xff0c;已成为我国转变信息服务业的发展新热点&#xff1a;成为信息通信领域发展最快、市场潜力最大的业务领域。互联网尤其是移动互联网&#xff0c;以其巨大的信息交换能力和快速渗透…

JVM——JDK 监控和故障处理工具总结

文章目录 JDK 命令行工具jps:查看所有 Java 进程jstat: 监视虚拟机各种运行状态信息 jinfo: 实时地查看和调整虚拟机各项参数jmap:生成堆转储快照**jhat**: 分析 heapdump 文件**jstack** :生成虚拟机当前时刻的线程快照 JDK 可视化分析工具JConsole:Java 监视与管理控制台连接…

Flink内核源码解析--Flink中重要的工作组件和机制

Flink内核源码 1、掌握Flink应用程序抽象2、掌握Flink核心组件整体架构抽象3、掌握Flink Job三种运行模式4、理解Flink RPC网络通信框架Akka详解5、理解TaskManager为例子&#xff0c;分析Flink封装Akka Actor的方法和整个调用流程6、理解Flink高可用服务HighAvailabilityServ…

【二分查找篇】速刷牛客TOP101 高效刷题指南

文章目录 17、BM17 二分查找-I18、BM18 二维数组中的查找19、BM19 寻找峰值20、BM20 数组中的逆序对21、BM21 旋转数组的最小数字22、BM22 比较版本号23、BM23 二叉树的前序遍历 17、BM17 二分查找-I 思路步骤&#xff1a; step 1&#xff1a;从数组首尾开始&#xff0c;每次取…

微服务中间件--分布式事务

分布式事务 a.理论基础1) CAP定理2) BASE理论 b.Seata1) XA模式1.a) 实现XA模式 2) AT模式3) TCC模式3.a) 代码实现 4) Saga模式5) 四种模式对比6) TC的异地多机房容灾架构 a.理论基础 1) CAP定理 分布式系统有三个指标&#xff1a; Consistency&#xff08;一致性&#xff…

GRPC 链接 NODE 和 GOLANG

GRPC 链接 NODE 和 GOLANG GRPC 了解 什么是GRPC gRPC 采用了 Protocol Buffers 作为数据序列化和反序列化的协议&#xff0c;可以更快速地传输数据&#xff0c;并支持多种编程语言的跨平台使用gRPC 提供“统一水平层”来对此类问题进行抽象化。 开发人员在本机平台中编写专…

使用本地电脑搭建可以远程访问的SFTP服务器

文章目录 1. 搭建SFTP服务器1.1 下载 freesshd 服务器软件1.3 启动SFTP服务1.4 添加用户1.5 保存所有配置 2. 安装SFTP客户端FileZilla测试2.1 配置一个本地SFTP站点2.2 内网连接测试成功 3. 使用cpolar内网穿透3.1 创建SFTP隧道3.2 查看在线隧道列表 4. 使用SFTP客户端&#x…

WXML中的条件语句

wx:if 和 hidden 的使用 1.在js中定义数据 Page({ data:{ type:1,flag: false}, }) 2.在wxml中使用wx:if 和 hidden&#xff0c; block用于包装组件(不会渲染为任何标签) <!-- 条件渲染 --><view wx:if"{{type 1}}">男</view><view wx:elif…

Wappalyzer - 技术剖析工具的必备浏览器扩展

目录 前言一、Wappalyzer简介1.Wappalyzer的背景和由来2.Wappalyzer的目标和优势 二、Wappalyzer的工作原理1.检测技术栈的方法和策略2.数据库和规则集的更新 三、如何使用Wappalyzer1.安装Wappalyzer浏览器扩展2.在浏览器中使用Wappalyzer进行技术剖析 总结 前言 在当今的数字…

很好的启用window10专业版系统自带的远程桌面

启用window10专业版系统自带的远程桌面 文章目录 启用window10专业版系统自带的远程桌面前言1.找到远程桌面的开关2. 找到“应用”项目3. 打开需要远程操作的电脑远程桌面功能 总结 前言 Windows操作系统作为应用最广泛的个人电脑操作系统&#xff0c;在我们身边几乎随处可见。…

SpringBoo t+ Vue 微人事 (十一)

职位修改操作 在对话框里面做编辑的操作 添加对话框 <el-dialogtitle"修改职位":visible.sync"dialogVisible"width"30%"><div><el-tag>职位名称</el-tag><el-input size"small" class"updatePosIn…

Linux学习之ssh和scp

ls /etc/ssh可以看到这个目录下有一些文件&#xff0c;而/etc/ssh/ssh_config是客户端配置文件&#xff0c;/etc/ssh/sshd_config是服务端配置文件。 cat -n /etc/ssh/sshd_config | grep "Port "可以看一下sshd监听端口的配置信息&#xff0c;发现这个配置端口是22…

ubuntu 编译安装nginx及安装nginx_upstream_check_module模块

如果有帮助到你&#xff0c;麻烦点个赞呗&#xff5e; 一、下载安装包 # 下载nginx_upstream_check_module模块 wget https://codeload.github.com/yaoweibin/nginx_upstream_check_module/zip/master# 解压 unzip master# 下载nginx 1.21.6 wget https://github.com/nginx/…

无涯教程-PHP - 循环语句

PHP中的循环用于执行相同的代码块指定的次数。 PHP支持以下四种循环类型。 for - 在代码块中循环指定的次数。 while - 如果且只要指定条件为真&#xff0c;就会循环遍历代码块。 do ... while - 循环执行一次代码块&#xf…

损失函数,基于概率分布度量的损失函数,信息量,信息熵的作用

目录 损失函数中为什么要用Log&#xff1a;概率损失函数-乘法转加法-便于求偏导 信息量&#xff0c;信息熵的作用 信息的作用是消除不确定性&#xff1a;信息量是0&#xff0c;事件确定 回答只是Y,N&#xff0c;因此对数底数为2​编辑 一句话描述的事件发生的概率越低&#…