一起学数据结构(5)——栈和队列

1. 栈的相关定义及特点:

1. 栈的相关定义:

在正式介绍栈的定义之前,首先来回顾一下关于线性表的定义:

线性表是具有相同数据类型的n(n>0)个数据元素的有限序列,其中n为表长。n=0时,可以把线性表看作一个空表,一个典型的线性表就是26英文字母组成的序列,即:

                                                (A,B,C,D,E......)

在之前介绍线性表的文章中,解释并实现了线性表的某些功能,例如:头插、尾删、任意位置插入结点等。对于线性表而言,其相对于链表的优点有可以随机访问结点。当利用线性表对任意位置插入结点时,其时间复杂度为O(N),会过于繁琐。

在上面简要给出线性表的相关内容后,下面给出栈的基本定义:

栈(Stack)是一种特殊的线性表,但是与上面所说明的线性表不同的是,栈是一种只能在表尾进行插入、删除操作的线性表。即:
                                       Stack: (a_{1},a_{2},a_{3},a_{4},a_{5}......a_{n})

对于上面给出的栈的简要示意图,将表尾(即a_{n})称之为栈顶Top,将表头(即a_{1})称之为栈底Base,因此,上面所提到栈是一种只能在表尾进行插入、删除的数据表这一概念,在这里也可以解释为,栈是一种只能在栈顶Top进行插入、删除操作的线性表。并且,将从栈顶Top插入元素的这一操作命名为进栈,将在栈顶Top进行删除的这一操作命名为出栈。

1.2 栈的特点及相关应用:

对于上面所提到的进栈、出栈这两个操作,可以通过下面的图形进行表示:

将下面给出的图形定义为空栈

由上面给出的关于栈底、栈顶的相关定义可知,因为此时的栈为空,所以,栈底、栈顶指向同一位置。

当元素a_{1}进行入栈操作时,栈、栈底、栈顶的变化可以用下面的图形进行表示:

在元素a_{1}完成入栈后,栈底Base不变,栈顶Top指向的位置发生变化。一般来说,栈顶Top用来记录栈中完成入栈的元素个数。

如果,再向上面给出的栈中入栈两个元素a_{2},a_{3}。即:

对上面的栈进行出栈操作时,由上面给出的关于出栈的定义可知,出栈的元素顺序为:a_{3},a_{2},a_{1}

所以,栈也可以看作一个具有后进先出特点的线性表

介于栈后进先出的这一特点,栈可以用于解决许多的实际问题,例如:数制转换、括号匹配检验、表达式求值等。在文章最后会详细解释括号匹配检验问题

2. 栈的代码实现(顺序栈的实现):

2.1 栈结构创建:

采用结构体对栈的结构进行创建,其中静态的栈结构如下:
 

#define N 10;
struct Stack
{int arr[N];int top;
};

在前面实现顺序表时就提到,在采用静态方式来实现栈或者顺序表等数据结构时,由于内存大小不能进行灵活的调整,很容易就会造成内存浪费或者越界等问题。本文依旧采用动态开辟内存的方式来实现对栈结构的创建。代码如下:
 

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

其中,top表示栈顶。用于后续的插入删除等操作的实现。capacity用于表示栈中被使用的空间大小,一旦使用的空间大小达到capacity,就立刻进行扩容。

2.2 栈的初始化:

定义函数STInit用于初始化上面创建的栈的结构。其中,需要进行的操作为:

1.动态开辟一定大小的空间。或者直接将结构体中创建的指针a初始化为NULL.后续进行扩容。因为在顺序表中采用了第一种方式。所以,对于栈的初始化,采用第二种

2.初始化时,栈为空栈,所以将topcapacity初始化为0

代码如下:
 

//栈的初始化:
void STInit( ST* ps )
{assert(ps);ps->a = NULL;ps->top = ps->capacity = 0;}

2.3 栈的销毁:

对于栈的销毁,同样可以分为下面几步:

1.free指针a所指向的动态开辟的空间。

2.将指针a中存储的地址改为NULL

3.将top,capacity都改为0

代码如下:

//栈的销毁:
void STDestory(ST* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->top = ps->capacity = 0;
}

2.4 通过栈顶向栈中插入元素:

对于通过栈顶向栈中插入元素这一功能,可以分为下面几步进行实现:

1.前面说到,为了演示扩容的第二种方式,所以在通过栈顶向栈中插入元素这一操作时,首先需要检查表示栈中已有元素数量的变量top是否与表示栈容量的变量capacity相等。若相等,则表示此时栈空间已满需要啊进行扩容。

2.在扩容完毕之后,需要将表示容量的变量capacity的大小进行更改。并且将用于扩容的指针变量中的值赋值给a

3.此时指针变量a中存储了动态开辟的空间的地址,通过ps->a[ps->top] = x来完成插入元素的目的。

4.将top+1

代码如下:
 

void STPush(ST* ps, STDataType x)
{assert(ps);if (ps->top == ps->capacity){int newcapacity = ps->capacity == 0 ? ps->capacity = 4: ps->capacity * 2;STDataType* newnode = (STDataType*)realloc(ps->a,sizeof(STDataType) * newcapacity);if (newnode == NULL){perror("realloc");}ps->a = newnode;ps->capacity = newcapacity;}ps->a[ps->top] = x;ps->top++;
}

2.5 删除栈中的元素:

在上面通过栈顶向栈中插入元素的操作中,ps->a[ps->top] = x;表示,插入元素时,是通过a[ps->top]来访问数组并且进行插入的。所以,对于删除栈中的元素。只需要将top-1即可。代码如下:

void STPop(ST* ps)
{assert(ps);assert(ps->top > 0);ps->top--;}

2.6 探空:

用于判断此时的栈是否为空栈,所以,只需要检测ps->top == 0即可,代码如下:

bool STEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}


2.7 求栈的长度:

对于栈的长度,也就是栈中插入了几个元素。可以通过栈顶top进行反应:
 

int size(ST* ps)
{assert(ps);return ps->top;
}

2.8 取栈顶元素:

与通过栈顶向栈中插入元素的大致思路相同,通过ps->a[top-1]达到取栈顶元素的目的,代码如下:

STDataType STTop(ST* ps)
{assert(ps);assert(ps->top > 0);return ps->a[ps->top-1];}

3. 与栈相关的题目解析——Leetcode.20——有效的括号:

3.1 题目解析:

题目要求在判断有效字符串时,需要满足相同类型的括号闭合,以及正确的闭合顺序。对于正确的闭合顺序这一要求,决定了题目不能使用数组来统计不同类型的括号的数量,判断相同类型阔号的数量是否为偶数来解决问题。

在栈的特点这一部分的内容中提到,栈可以看作有后进先出特点的线性表。介于这个特点可以用栈来解决此题。

具体思路如下:

1. 采用while循环对给定字符串的每个字符遍历,检测被遍历的字符是否为三个括号:‘(‘,’[’,‘{’其中之一,满足条件则将这个字符入栈。

2. 当遇到字符串为‘)’,‘]’,‘}’,时,将栈中已经记录的字符出栈,并且额外创建一个变量记录。进行匹配。如果此时遇到的字符串与出栈的字符串不满足题目中给定的关系,即不满足每个右括号都有一个对应的相同类型的左括号。则返回false。如果满足则让*s指向下一个位置。如果整体字符串都满足上述的对应关系。则返回true

例如,对于字符串"( { { [ ] } } )"

按照上面所说的步骤,首先将满足(’,’[’,‘{’其中之一,满足条件则将这个字符入栈。。此时,栈内的情况可有下面的图进行表示:

这一过程可由下面的代码实现:

while( *s){switch(*s){case '(':case '[':case '{':STPush(&ps,*s);break;}*s++;}

当遍历过程中遇到了右括号,及”] } } )",开始进行匹配,先创建一个临时变量cur用于记录出栈的元素。利用STTop取出栈顶元素记录在cur同时,为了下次循环时可以读取到栈后续的内容,需要利用STop删除这个元素。

在进行匹配时,只需要考虑匹配不成功的情况。并返回false。对于匹配不成功的情况,即左右括号不对称。可以由下面的代码表示:
 

 cur = STTop(&ps);STPop(&ps);if( (*s == '}' && cur !='{') || ((*s == ']') && (cur != '[')) || ((*s ==')'))&& (cur != '(')){STDestory(&ps);return false;}break;

当字符串中每一个被遍历的字符都匹配成功,说明该字符串是题目要求的有效字符串。不过,再返回true之前需要考虑两个特殊情况:
1. 字符串是否只存在左括号,即‘(‘,’[’,‘{’

2. 字符串是否只存在右括号,即‘)’,‘]’,‘}’

3.字符串中左右括号的数量是否相同。

对于情况1,因为不存在右括号,所以在循环的第一部分,即入栈后,就会跳出循环,不参与后续的匹配。所以只需要利用STEmpty检测此时的栈是否为空即可。不为空则说明,左右括号数目不相同或者不存在右括号。

对于情况2.因为不存在左括号,所以在循环中经历入栈这个过程时,栈为空。只需要在入栈这个步骤结束后,检测栈是否为空即可。为空则返回false即可

对于情况3,在情况一中得到解决。

3.2 代码展示:

(注:79及79行之前的内容为栈的代码实现)

typedef char STDataType;
typedef struct Stack
{STDataType* a;int top;int capacity;
}ST;void STInit( ST* ps )
{assert(ps);ps->a = NULL;ps->top = ps->capacity = 0;
}//栈的销毁:
void STDestory(ST* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->top = ps->capacity = 0;
}void STPush(ST* ps, STDataType x)
{assert(ps);if (ps->top == ps->capacity){int newcapacity = ps->capacity == 0 ? ps->capacity = 4: ps->capacity * 2;STDataType* newnode = (STDataType*)realloc(ps->a,sizeof(STDataType) * newcapacity);if (newnode == NULL){perror("realloc");}ps->a = newnode;ps->capacity = newcapacity;}ps->a[ps->top] = x;ps->top++;
}void STPop(ST* ps)
{assert(ps);assert(ps->top > 0);ps->top--;}int size(ST* ps)
{assert(ps);return ps->top;
}bool STEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}STDataType STTop(ST* ps)
{assert(ps);assert(ps->top > 0);return ps->a[ps->top-1];}bool isValid(char * s){ST ps;STInit( &ps);char cur;while( *s){switch(*s){case '(':case '[':case '{':STPush(&ps,*s);break;//匹配'{'case'}':case']':case')'://检测是否存在只有右边有括号的情况if( STEmpty(&ps)){STDestory(&ps);return false;}//取栈顶元素cur = STTop(&ps);STPop(&ps);if( (*s == '}' && cur !='{') || ((*s == ']') && (cur != '[')) || ((*s ==')'))&& (cur != '(')){STDestory(&ps);return false;}break;}*s++;}//检测是否只有左边有括号的情况,因为在匹配括号时,如果存在右括号//会使用STTop吸收左括号,所以,如果ret为0,则表示左括号全部吸收完。bool ret = STEmpty(&ps);STDestory(&ps);return ret;}

结果如下:


 

4. 栈的代码补充:

上面给出的栈并不全面,下面给出头文件Stack.h

#pragma once#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//静态栈的创建:
//#define N 10;
//struct Stack
//{
//	int arr[N];
//	int top;
//};//栈的动态开辟:
typedef int STDataType;
typedef struct Stack
{STDataType* a;int top;int capacity;
}ST;//栈的初始化:
void STInit(ST* ps);//栈的销毁
void STDestory(ST* ps);//通过栈顶向栈中插入元素
void STPush(ST* ps, STDataType x);//删除栈中的元素:
void STPop(ST* ps);//记录size
int size(ST* ps);//找空
bool STEmpty(ST* ps);//获取栈顶元素
STDataType STTop(ST* ps);


 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SwiftUI 内功加持:“曳光弹“实现自定义样式进度条(ProgressView)

概览 虽然 SwiftUI 已为我们内置了很多常用视图&#xff0c;不过有时我们还是需要根据实际来进一步美化显示或增加功能。 如上图所示&#xff0c;在本篇博文中我们将结合敏捷哲学中一个超级实用的开发技巧&#xff1a;曳光弹&#xff0c;来一步一个脚印循序渐进的实现 Progres…

redisson分布式锁

RLock官网解释 基于Redis的Java分布式可重入锁对象&#xff0c;实现了锁接口。 如果获得锁的Redisson实例崩溃&#xff0c;那么这种锁可能永远挂起在获得状态。为了避免这种情况&#xff0c;Redisson维护了锁看门狗&#xff0c;它在锁持有者Redisson实例活着的时候延长锁过期时…

逻辑回归(Logistic Regression)

1.分类问题 在分类问题中&#xff0c;你要预测的变量 y是离散的值&#xff0c;我们将学习一种叫做逻辑回归 (Logistic Regression) 的算法&#xff0c;这是目前最流行使用最广泛的一种学习算法。 在分类问题中&#xff0c;我们尝试预测的是结果是否属于某一个类&#xff08;例…

MultipartFile是什么

Multipart是一种file的类型 在我们进行文件上传时所发出的请求&#xff0c;我们页面对请求格式有明确的要求: 1.post提交表单方式 2.编码格式enctype必须是muitipart/form-data&#xff0c;这种格式适合传输数据量大的二进制数据文件 3.类型必须是file类 流程举例&#xf…

软件测试报告有什么用?

报告类型 不同的报告类型有不同的报告用途&#xff0c;以下分类别进行分析 1、登记测试报告 可以用于软件产品的增值税即征即退、软件企业的双软评估以及计算机系统集成资质的材料 2、鉴定\确认测试报告 可以用用于政府项目申报、高新认证、项目结题、创新产品认定、各类政…

Excel怎么批量生成文件夹

Excel怎么批量生成文件夹的链接: https://jingyan.baidu.com/article/ea24bc398d9dcb9b63b3312f.html

C 风格文件输入/输出---直接输入/输出---(std::fread)---(std::fwrite)

C 标准库的 C I/O 子集实现 C 风格流输入/输出操作。 <cstdio> 头文件提供通用文件支持并提供有窄和多字节字符输入/输出能力的函数&#xff0c;而 <cwchar>头文件提供有宽字符输入/输出能力的函数。 从直接输入/输出 文件读取 std::fread 从给定输入流 stream …

MMDetection实验记录踩坑记录

AP值始终为0 在实验MMDetection的DAB-DETR模型进行实验时&#xff0c;AP值始终上不去。 可以看到&#xff0c;在第22个epoch时的AP值仅为0.002 因为在此之前已经运行过YOLOX,Faster-RCNN等模型&#xff0c;所以数据集的设置肯定是没有问题的&#xff0c;而博主也只是修改了DAB…

Qt包含文件不存在问题解决 QNetworkAccessManager

这里用到了Qt的网络模块&#xff0c;在.pro中添加了 QT network 但是添加 #include <QNetworkAccessManager> 会报错说找不到&#xff0c;可以通过在项目上右键执行qmake后&#xff0c;直接#include <QNetworkAccessManager>就不会报错了&#xff1a;

java获取jenkins发布版本信息

一.需求&#xff1a; 系统cicd发布时首页需要展示jenkins发布的版本和优化内容 二.思路: 1.jenkins创建用户和秘钥 2.找到对应构建任务信息的api 3.RestTemplate发起http请求 三.实现&#xff1a; 1.创建用户和token 2.查找jenkins API 创建 Job POST http://localhost…

Flask狼书笔记 | 06_电子邮件

文章目录 6 电子邮件6.1 使用Flask-Mail发送6.2 使用事务邮件服务SendGrid6.3 电子邮件进阶6.4 小结 6 电子邮件 Web中&#xff0c;我们常在用户注册账户时发送确认邮件&#xff0c;或是推送信息。邮件必要的字段包含发信方(sender)&#xff0c;收信方(to)&#xff0c;邮件主题…

【vue2第十四章】 插槽(普通插槽、具名插槽、作用域插槽语法)

插槽 插槽是什么&#xff1f; 在 Vue 2 中&#xff0c;插槽&#xff08;slot&#xff09;是一种用于定义组件内部内容分发的机制。它允许你将组件中的一部分内容替换为用户自定义的内容&#xff0c;并在组件内部进行渲染。 通过在组件模板中使用 <slot></slot> 标…

常见IO模型(非常详细)

背景知识 常⽤5中⽹络IO模型 阻塞IO&#xff08;Blocking IO&#xff09;⾮阻塞IO&#xff08;Non-Blocking IO&#xff09;多路复⽤IO&#xff08;IO Multiplexing&#xff09;信号驱动IO&#xff08;Signal Driven IO&#xff09;异步IO&#xff08;Asynchronous IO&#x…

纯css实现奥运五环、3D平移、旋转、扭曲

文章目录 前言效果图htmlcss 前言 1、不是真正的五环&#xff0c;因为通过形变得来。 2、不同电脑显示器的像素不同&#xff0c;显现的效果不同。 3、不推荐使用此方法。 4、主要通过旋转加平移的方式实现。 效果图 html <div class"olympic_rings"><span …

我眼中的《视觉测量技术基础》

为什么会写这篇博客&#xff1a; 首先给大家说几点&#xff1a;看我的自我介绍对于学习这本书没有任何帮助&#xff0c;如果你是为了急切的想找一个视觉测量的解决方案那可以跳过自我介绍往下看或者换一篇博客看看&#xff0c;如果你是刚入门想学习计算机视觉的同学&#xff0…

【HTML/CSS】入门导学篇

本文属于HTML/CSS专栏文章&#xff0c;适合WEB前端开发入门学习&#xff0c;如果有所帮助请一键三连支持&#xff0c;对博主系列文章感兴趣点击下方专栏了解详细。 本文内容出自B站pink老师的前端入门教程&#xff0c;感谢pink老师&#xff01;&#xff01;&#xff01; 视频链…

【C++】封装map和set(红黑树实现)

前言&#xff1a; 前面&#xff0c;我们学习了set和map的用法&#xff0c;这两个容器可以完成查找&#xff0c;排序等操作&#xff0c;后来我们在学习过二叉搜索树的基础上又学习了两种特殊的二叉搜索树——AVL树和红黑树&#xff0c;他们俩可以是效率进一步提高&#xff0c;其…

Spring Security OAuth2 远程命令执行漏洞

文章目录 一、搭建环境二、漏洞验证三、准备payload四、执行payload五、变形payload 一、搭建环境 cd vulhub/spring/CVE-2016-4977/ docker-compose up -d 二、漏洞验证 访问 http://192.168.10.171:8080/oauth/authorize?response_type${233*233}&client_idacme&s…

【安全】正则回溯绕过练习简单案例

目录 环境 案例1 前要 代码审计 分析 案例2 代码审计 分析 payload 环境 phpstudy 案例1 前要 php中0 1 -1 true false null 空字符 数组之间的比较 代码审计 <?php function areyouok($greeting){return preg_match(/Merry.*Christmas/is,$greeting); //2.传…

YAML配置文件

YAML配置文件 SpringBoot中application.properties文件存在的问题&#xff1a;配置太多后难阅读和修改&#xff0c;层级结构辨识度不高。 简介 YAML是"YAML Ain’t a Markup Language"&#xff08;YAML不是一种标记语言&#xff09;的递归缩写。在开发的这种语言时&a…