【数据结构学习笔记 之 栈和队列】——上

news2024/12/23 9:43:39

前言:栈和队列是常用的数据结构之一,本文主要介绍有关栈的基本特性以及基本操作和一些经典的OJ题目,关于队列的介绍放到下篇。那么话不多说,让我们开始吧。

一、栈的基本知识

1. 栈的基本概念

栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出 LIFO(Last In First Out) 的原则。
下面是简单的示意图,请看:
入栈时,蓝色方块先入栈后,黄色方块再入栈
在这里插入图片描述
在这里插入图片描述
出栈时,黄色方块先出栈,蓝色方块再出栈:
在这里插入图片描述
在这里插入图片描述

2. 栈的存储方式

由概念可知,栈是一种特殊的线性表,故存储方式自然也就分为两种,顺序存储顺序栈)和链式存储链栈)。关于具体实现细节与其常用的存储方式会在下一部分进行说明。

二、栈的结构定义

1. 顺序栈

即通过顺序表,也就是数组的形式实现栈,其结构体定义如下:

typedef struct Stack
{
	STDataType* a;	//动态栈,空间不够则进行分配
	int top;		//栈顶指针
	int capacity;	//栈的容量
}Stack;

2. 链栈

即通过链表的形式实现栈,其结构体定义如下:

//单链表节点的结构定义
typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SListNode;

//链栈的结构定义
typedef struct Stack
{
	SListNode* top;		//栈顶指针
	int capacity;	//栈的容量
}Stack;

关于链栈还有一点需要注意的是:由于栈先进后出的特性,需要将单链表的第一个节点作为栈顶指针

说明一下:关于栈的存储方式我们一般使用的是顺序存储结构,因为其相比于链式存储结构来说,操作简单,便于实现,而且很关键的一个原因就在于栈的特性是 “先进后出”,即只能在栈顶一端进行删除,这就很好利用了顺序表尾删的便捷,又规避了顺序表除尾删外其他位置删除需要挪动元素的不便

文章接下来关于栈的介绍也建立在顺序栈的基础上。

三、栈的基本操作

前言:在进行基本操作的介绍之前,这里先对栈顶指针的具体指向进行一个说明。一般来说,栈顶指针可以指向栈顶元素,也可以指向栈顶元素的下一个位置;前者需要将栈顶指针初始化为-1,后者需要将栈顶指针初始化为0。
参考示意图如下:
在这里插入图片描述
本文对栈的介绍采用的是栈顶指针指向栈顶元素的下一个位置的模式。

1. 栈的初始化

通过初始化操作为栈开辟空间,并将相应结构成员信息进行初值设置。
(1)操作执行的过程

  • 为栈动态开辟一块内存空间,开辟空间的大小可通过define定义常量进行设定;
  • 将栈的容量初始化为设定的常量值;
  • 将栈顶指针初始化为0;

(2)具体代码

void StackInit(Stack* ps)
{
	assert(ps);
	
	//开辟空间
	STDataType* tmp = (STDataType*)malloc(sizeof(STDataType) * DEF_CAP);
	if (tmp == NULL)
	{
		perror("malloc failed");
		return;
	}
	ps->a = tmp;
	//初始化
	ps->capacity = DEF_CAP;
	ps->top = 0;		//top指向栈顶元素的下一个元素
}

(注:代码中的DEF_CAP是由define定义的常量,表示默认的容量值)

2. 栈的销毁

有建立肯定需要配套一个销毁;在栈用完时需要对其进行销毁(将空间还给操作系统)
(1)操作执行的过程

  • 将在初始化操作中所开辟的空间进行释放;
  • 将相应结构成员信息进行置零;

(2)具体代码

void StackDestroy(Stack* ps)
{
	assert(ps);

	//释放开辟的空间,并置空
	free(ps->a);
	ps->a = NULL;
	//相应结构成员信息置零
	ps->capacity = 0;
	ps->top = 0;
}

3. 判断栈是否为空

(1)操作的执行过程

  • 对栈顶指针进行判断即可,由于采用的是栈顶指针指向的栈顶元素下一个位置的模式,故判断栈顶指针是否等于0即可

(2)具体代码

bool IsStackEmpty(Stack* ps)
{
	assert(ps);

	return (ps->top == 0);
}

4. 入栈

(1)操作的执行过程

  • 通过栈顶指针判断栈空间是否已满,若已满,则需进行扩容,扩容的大小也可通过define定义常量进行设定;扩容完毕后,注意更新栈的容量信息;
  • 将值存放到栈顶指针指向位置所对应的空间后,栈顶指针再进行++操作;

(2)具体代码

void StackPush(Stack* ps, STDataType x)
{
	assert(ps);

	//检查栈是否已满
	if (ps->top == ps->capacity)
	{
		STDataType*tmp = (STDataType*)realloc(ps->a,  sizeof(STDataType)*(ps->capacity+INC_CAP));
		if (tmp == NULL)
		{
			perror("realloc failed");
			return;
		}
		ps->a = tmp;
		//注意更新栈的容量信息
		ps->capacity += INC_CAP;
	}
	//进行赋值后栈顶指针再++
	ps->a[ps->top++] = x;
}

(注:代码中的INC_CAP为define定义的常量,表示扩容量的值)

5. 出栈

(1)操作执行过程:

  • 进行出栈前需先进行栈是否为空的判断,若为空则也通过assert断言进行报错
  • 由于是通过顺序表(数组)来实现栈,故控制栈的访问权限就只取决于栈顶指针top的位置,即出栈并不是真正将栈顶的元素“ 删除” 或 “释放”掉,而直接让栈顶指针的--而不去访问那个空间即可。

(2)具体代码:

void StackPop(Stack* ps)
{
	assert(ps);
	assert(!IsStackEmpty(ps));

	ps->top--;
	//直接让栈顶指针--即可
}

6. 访问栈顶元素

(1)操作执行过程:

  • 先判断栈是否为空,若为空则需通过assert断言报错;
  • 通过栈顶指针将栈顶元素(数组中最后一个元素)的值返回即可。

(2)具体代码:

STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(!IsStackEmpty(ps));

	return ps->a[ps->top - 1];
	//栈顶指针指向栈顶元素的下一个位置
}

如上就是一些栈的基本操作啦,关于栈部分完整的头文件以及源文件大家如果有需要的话可以从博主的码云仓库中自行获取,这里是链接:DataStructure
掌握了这些基本操作后我们就可以尝试着去解决一些问题了,那么下面我们开始进入实战环节。

四、关于栈的OJ

1. 括号匹配问题

原题链接在这:
有效的括号
解题思路
字符串中的左括号一定会和字符串中于其相隔最近的右括号进行匹配,结合栈先进后出的特点,我们可以每当遇到左括号时,就让该左括号入栈;每当遇到右括号时就让栈顶的左括号出栈来和该右括号进行匹配,若匹配成功则说明该括号有效,继续进行下一组匹配;若匹配失败,则直接返回;

由解题思路可将具体的操作过程归结为两步:

  • 遍历字符串,每遇到左括号就让该左括号入栈
  • 每遇到右括号就让栈顶元素的左括号出栈,与其进行匹配;

有了具体的操作后,还有一些需要注意的特殊情况

  • 字符串中可能只有左括号或者左括号的数量多于右括号,此时不属于有效的匹配;由于遍历完字符串后括号都为有效匹配栈一定为空,故可在遍历完字符串后判断栈是否为空来解决这个问题;
  • 字符串中可能只有右括号或者第一个字符就为右括号,此时不属于有效的匹配;由于匹配需要进行出栈操作,而此时的栈一定为空栈,故可通过判断栈是否为空来解决这个问题;

结合以上说明。我们就可得到具体代码如下:

//1.左括号就入栈
//2.右括号就出栈与其进行匹配
typedef struct Stack
{
	char* a;
	int top;
}Stack;

//初始化
void StackInit(Stack* ps)
{
	char* tmp = (char*)malloc(sizeof(char) * 10001);
	ps->a = tmp;
	ps->top = -1;		
}

//入栈
void StackPush(Stack* ps, char x)
{
	ps->a[++ps->top] = x;
}

//出栈
char StackPop(Stack* ps)
{
	return ps->a[ps->top--];
}

bool isValid(char * s)
{
    if(strlen(s) < 2)
        return false;

    Stack st;
    StackInit(&st);
    char* mov = s;
    while(*mov)
    {
        if((*mov == '(') || (*mov == '[') || (*mov == '{'))
        {
            StackPush(&st,*mov);
        }
        else
        {
            if(st.top == -1)    //第一个就是右括号
                return false;
            
            if(*mov == ')' && StackPop(&st) != '(')
                return false;
            if(*mov == ']' && StackPop(&st) != '[')
                return false;
            if(*mov == '}' && StackPop(&st) != '{')
                return false;         
        }
        mov++;
    }

    if(st.top != -1)    //有效字符串匹配完成后栈一定为空,即top为-1
        return false;
        
    return true;
}

(PS:解题代码中的出栈操作和在对栈的基本操作的介绍中的操作有一些区别:解题代码中的出栈操作相当于将访问栈顶元素和出栈结合起来了,即可理解为先获取了栈顶元素的信息,然后进行了出栈操作。)

本章完。

看完觉得有觉得帮助的话不妨点赞收藏鼓励一下,有疑问或有误地方的地方还恳请过路的朋友们留个评论,多多指点,谢谢朋友们!🌹🌹🌹

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

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

相关文章

同学在外包干了两年的点点点,24岁人就快废了

前言 简单的说下&#xff0c;我大学的一个同学&#xff0c;毕业后我自己去了自研的公司&#xff0c;他去了外包&#xff0c;快两年了我薪资、技术各个方面都有了很大的提升&#xff0c;他在外包干的这两年人都要废了&#xff0c;技术没一点提升&#xff0c;学不到任何东西&…

JavaScript 的学习

文章目录一、简介总结一、简介 JavaScript 是互联网上最流行的脚本语言&#xff0c;这门语言可用于 HTML 和 web&#xff0c;更可广泛用于服务器、PC、笔记本电脑、平板电脑和智能手机等设备。 JavaScript 是脚本语言 JavaScript 是一种轻量级的编程语言。 JavaScript 是可插入…

如果要向“硅谷精神之父”提一道问题,你会问什么?| CSDN 访谈世界互联网教父 Kevin Kelly

ChatGPT 的问世不禁让人遐想&#xff0c;接下来的 5000 天&#xff0c;将会发生什么事&#xff1f; 硅谷精神之父、世界互联网教父、《失控》作者凯文凯利&#xff08;Kevin Kelly&#xff0c;以下简称 K.K.&#xff09;是这样预测的&#xff1a; 未来将会是一切都与 AI 相连的…

Vue3通知提醒框(Notification)

Vue3相关组件项目依赖版本信息 可自定义设置以下属性&#xff1a; 消息的标题&#xff08;title&#xff09;&#xff0c;默认温馨提示自动关闭的延时时长&#xff08;duration&#xff09;&#xff0c;单位ms&#xff0c;默认4500ms消息从顶部弹出时&#xff0c;距离顶部的位…

【问题】开发遇到的小问题

文章目录使用糊涂工具&#xff0c;将时间字符串转化为LocalDateTime类型Date类型转换LocalDate类型jdk8 LocalDateTime获取当前时间和前后推时间echarts图中显示表格是需要添加宽高前端往后端传值时&#xff0c;需要转一下对象再往后端传使用 value-format"yyyy-MM-dd HH:…

jwt授权

JWT格式 由header、payload、signature三部分组成&#xff0c;中间用圆点(.)连接: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJpYW0uYXBpLm1hcm1vdGVkdS5jb20iLCJleHAiOjE2NDI4NTY2MzcsImlkZW50aXR5IjoiYWRtaW4iLCJpc3MiOiJpYW0tYXBpc2VydmVyIiwib3JpZ19pYXQiOjE2MzUw…

拼团系统开发|全民拼购商业模式解读

拼团系统开发|拼团模式在市场上已经屡见不鲜&#xff0c;某夕夕就是这方面的典范。不过现在市场上又出现一种升级版的拼团商业模式&#xff0c;也就是全民拼购&#xff0c;它除了可以帮助商家提升销量&#xff0c;还有引流裂变获客的效果&#xff0c;因此受到了许多企业商家的热…

chatgpt教你练习前端算法

今天想试试chatgpt关于代码算法这一块儿是否好用。 判断质数 上面的代码有一点小问题&#xff0c;当num为2时&#xff0c;返回的结果是错误的&#xff0c;我改进了一下&#xff0c;并优化了一点性能 // 判断是否是素数&#xff08;质数&#xff09; function isprime(number)…

【Linux】用LCD文字祝愿(Framebuffer+Freetype)

目录 前言 一、LCD操作原理 &#xff08;1&#xff09;LCD和Framebuffer。 &#xff08;2&#xff09;LCD的操作&#xff1a; &#xff08;3&#xff09;核心函数&#xff08;后续也会经常用到&#xff09; ①open函数 ②ioctl函数 ③mmap函数 二、字符的点阵显示 &a…

4K高清修复,模糊视频4k修复是怎么实现的?

在当今数字时代&#xff0c;高分辨率视频已成为大众观影的标配。4K分辨率作为其中高端的选项&#xff0c;提供了比传统1080p高出四倍的细节和清晰度&#xff0c;使得观众们能够更加身临其境地享受影视作品。然而&#xff0c;有时候我们可能会遇到4K视频质量不佳的问题&#xff…

Chapter3-用适合的方式发送和接收消息

3.1 不同类型的消费者 消费者可分为两种类型。 一个是DefaultMQPushConsumer &#xff0c;由系统控制读取操作&#xff0c;收到消息后自动调用传人的处理方法来处理&#xff1b;另 一个是 DefaultMlConsumer &#xff0c;读取操作中的大部分功能由使用者自主控制 。 3.1.1 Def…

uni-app:登录与支付--用户信息

用户信息 实现用户头像昵称区域的基本布局 在 my-userinfo 组件中&#xff0c;定义如下的 UI 结构&#xff1a; <template><view class"my-userinfo-container"><!-- 头像昵称区域 --><view class"top-box"><image src"…

如何通过小程序容器技术实现App的灰度发布

在当今移动应用市场竞争激烈的环境下&#xff0c;如何更快地发布新版本、更精确地测试和调整、更好地了解用户需求和行为&#xff0c;成为了每个App开发者面临的重要挑战。在这个背景下&#xff0c;灰度发布和小程序容器技术成为了越来越受欢迎的解决方案。 灰度发布是指将新版…

【Linux高性能服务器编程】I/O复用的高级应用

文章目录一、基于 select 的非阻塞 connect二、基于 poll 的聊天室程序2.1 客户端2.2 服务器三、基于 epoll 实现同时处理 TCP 和 UDP 服务一、基于 select 的非阻塞 connect connect系统调用的 man 手册中有如下一段内容&#xff1a; EINPROGERESS The socket is nonblocking…

ChatGPT既然这么火,有没有弊端呢?

介绍 在现代社会中&#xff0c;人们越来越依赖技术来解决问题。聊天机器人是一种最新的技术趋势&#xff0c;这种技术可以为人们带来很多便利。而ChatGPT聊天机器人则是其中的一种&#xff0c;它使用了大型的语言模型GPT&#xff08;Generative Pre-trained Transformer&#…

实操干货丨ChatGPT+XMind,效率爆炸,天下无敌

日常办公中&#xff0c;常用工具我们最熟悉的莫过于office铁三角&#xff1a;Word/Excel/PPT&#xff0c;除了这哥仨&#xff0c;再让你推荐一个。 XMind&#xff0c;绝对位列其中。 今天给大家分享一个 ChatGPTXMind的绝佳玩法&#xff0c;不需要下载安装任何额外的工具。 …

【MySQL | 基础篇】05、MySQL 事务详解

目录 一、事务简介 二、事务操作 2.1 未控制事务 2.2 控制事务一 2.3 控制事务二 三、事务四大特性 四、并发事务问题 五、事务隔离级别 六、并发事务演示 6.1 脏读演示 6.2 不可重复读演示 6.3 幻读演示 一、事务简介 事务是一组操作的集合&#xff0c;它是一个不…

【云原生】Kubernetes 中容器跨主机网络是怎么样的?

文章目录前言什么是 FlannelFlannel 的后端实现有哪些UDPVXLANHost-gw基于 Flannel UDP 模式的实现跨主通信UDP 模式案例实现基于 Flannel VXLAN 模式的跨主通信VXLAN 模式案例实现总结前言 在云原生领域&#xff0c;Kubernetes 已经成为了最主流的容器管理工具。Kubernetes 支…

JMM内存模型详解

1、概要 JMM全称叫Java Memory Model&#xff08;Java内存模型&#xff09;&#xff0c;什么是JMM&#xff0c;为什么要设置JMM&#xff0c;要弄清楚这个&#xff0c;咱们要先从计算机硬件存储体系说起。 2、计算机硬件存储体系 Window任务管理器Mac资源管理器window和mac是两…

Flink 优化 (二) --------- 状态及 Checkpoint 调优

目录一、RocksDB 大状态调优1. 开启 State 访问性能监控2. 开启增量检查点和本地恢复3. 调整预定义选项4. 增大 block 缓存5. 增大 write buffer 和 level 阈值大小6. 增大 write buffer 数量7. 增大后台线程数和 write buffer 合并数8. 开启分区索引功能9. 参数设定案例二、Ch…