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

本章重点

  • 栈的概念及结构

  • 栈的实现方式

  • 数组实现栈接口

  • 栈面试题目

  • 概念选择题

一、栈的概念及结构

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出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,功能相当与mallocSTDataType* 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;elsereturn 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;elsereturn 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出栈,可能。

本章结束啦!!!

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

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

相关文章

指针(一)【C语言进阶版】

大家好,我是深鱼~ 【前言】: 指针的主题,在初阶指针章节已经接触过了,我们知道了指针的概念: 1.指针就是个变量,用来存放地址,地址的唯一标识一块内存空间(指针变量)&a…

【云原生|Docker系列第3篇】Docker镜像的入门实践

欢迎来到Docker入门系列的第三篇博客!在前两篇博客中,我们已经了解了什么是Docker以及如何安装和配置它。本篇博客将重点介绍Docker镜像的概念,以及它们之间的关系。我们还将学习如何拉取、创建、管理和分享Docker镜像,这是使用Do…

jenkins同一jar包部署到多台服务器

文章目录 安装插件配置ssh服务构建完成后执行 没有部署过可以跟这个下面的步骤先部署一遍,我这篇主要讲jenkins同一jar包部署到多台服务器 【Jenkins】部署Springboot项目https://blog.csdn.net/qq_39017153/article/details/131901613 安装插件 Publish Over SSH 这…

stm32g070的PD0/PD2 PA8和PB15

目前在用STM32G070做项目,其中PD2TIMER3去模拟PWM,PD0用作按键检测,测试发现PD0低电平检测没有问题,高电平检测不到,电路图如下图所示: 用万用表测试电平,高电平1.0V左右,首先怀疑硬…

PyTorch安装教程:从头开始配置PyTorch环境

PyTorch是一个开源的机器学习框架,广泛用于深度学习任务。要开始使用PyTorch,您需要在计算机上正确配置PyTorch环境。本文将为您提供一步步的指南,帮助您成功安装和配置PyTorch。 第一部分:安装Python和相关工具 第一步&#xf…

rust踩雷笔记(4)——刷点Vec相关的题(持续更新)

俗话说,孰能生巧,今天是第六天接触Rust,感觉基础语法和特性没什么问题了(当然如果你整天都学这个可能2天半就够了),但是想达到熟练使用,还需要刷点题。算法我相信能来看rust博客的人都是大牛&am…

【项目实践】基于LSTM的一维数据扩展与预测

基于LSTM的一维数据拟合扩展 一、引(fei)言(hua) 我在做Sri Lanka生态系统服务价值计算时,中间遇到了一点小问题。从世界粮农组织(FAO)上获得Sri Lanka主要农作物产量和价格数据时,其中的主要作物Sorghum仅有2001-2006年的数据,而Millet只有…

算法通关村第4关【黄金】| 表达式问题

1. 计算器问题 思路:此题不考虑括号和负数情况,单纯使用栈即可解决。注意的是数字可能是多位数需要保留完整的num, 保留数字的前缀符号,当碰到加号,存进去;当碰到减号,存相反数进去;…

Apinto 网关进阶教程,插件开发入门指南

Apinto 是基于Go语言,由 Eolink 自主研发的一款高性能、可扩展、易维护的云原生 API 网关。Apinto 能够帮助用户简单、快速、低成本、低风险地实现:系统微服务化、系统集成、向合作伙伴、开发者开放功能和数据。 通过 Apinto,企业能够专注于…

clickhouse修改默认密码

1.明文密码 vim /etc/clickhouse-server/users.xml找到下面的语句,增加明文密码 <password>123456789</password> 2. sha256密码 # echo -n 123456789 | openssl dgst -sha256 (stdin) 15e2b0d3c33891ebb0f1ef609ec419420c20e320ce94c65fbc8c3312448eb225 修改…

js new关键字的作用

创建一个新对象&#xff0c;如&#xff1a;var person {};新对象的_proto_属性指向构造函数的原型对象。将构造函数的作用域赋值给新对象。&#xff08;也所以this对象指向新对象&#xff09;执行构造函数内部的代码&#xff0c;将属性添加给person中的this对象。返回新对象pe…

MobileNet、MobileNetV2和MobileNetV3创新点总结

当谈论MobileNet、MobileNetV2和MobileNetV3时&#xff0c;我们指的是一系列基于深度学习的轻量级神经网络架构&#xff0c;这些架构旨在在保持高度准确性的同时减少模型的计算和参数量。以下是它们各自的创新点的详细总结&#xff1a; MobileNet&#xff1a; 深度可分离卷积&…

【LeetCode-中等题】15. 三数之和

题目 题解一&#xff1a;双指针法 图解参考链接&#xff1a;画解算法&#xff1a;15. 三数之和 详解参考代码随想录讲的非常好 梦破碎的地方&#xff01;| LeetCode&#xff1a;15.三数之和 代码&#xff1a; class Solution {public List<List<Integer>> thre…

Codeforces Round 893 (Div. 2) A ~ C

比赛链接 A. Buttons 博弈、最优策略一定是先去按都能按的按钮&#xff0c;按完之后再按自己的。 #include<bits/stdc.h> #define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); #define endl \nusing namespace std;typedef pair<int, int> PII; typede…

Python使用OpenCV库对彩色图像进行通道分离

目录 1、解释说明&#xff1a; 2、使用示例&#xff1a; 3、注意事项&#xff1a; 1、解释说明&#xff1a; 在Python中&#xff0c;我们可以使用OpenCV库对彩色图像进行通道分离。通道分离是将彩色图像的每个像素分解为三个通道&#xff08;红、绿、蓝&#xff09;的过程。…

.eslintrc配置

ESLint 标准规则 /*** AlloyTeam ESLint 规则** 包含所有 ESLint 规则* 使用 babel-eslint 作为解析器** fixable 表示此配置支持 --fix* off 表示此配置被关闭了&#xff0c;并且后面说明了关闭的原因*/module.exports {parser: babel-eslint,parserOptions: {ecmaVersion: 2…

jstack(Stack Trace for Java)Java堆栈跟踪工具

jstack&#xff08;Stack Trace for Java&#xff09;Java堆栈跟踪工具 jstack&#xff08;Stack Trace for Java&#xff09;命令用于生成虚拟机当前时刻的线程快照&#xff08;一般称为threaddump或者javacore文件&#xff09;。 线程快照就是当前虚拟机内每一条线程正在执…

Android 应用冷启动优化

冷启动相关概念 应用启动概念 冷启动&#xff1a;首次打开app或者app彻底销毁后再次打开app&#xff08;开关机后&#xff09;&#xff0c;这也是我们进行启动速度优化的主要方向。热启动&#xff1a;应用运行中按home键再打开应用。温启动&#xff1a;介于两者之间&#xff…

动手学深度学习-pytorch版本(二):线性神经网络

参考引用 动手学深度学习 1. 线性神经网络 神经网络的整个训练过程&#xff0c;包括: 定义简单的神经网络架构、数据处理、指定损失函数和如何训练模型。经典统计学习技术中的线性回归和 softmax 回归可以视为线性神经网络 1.1 线性回归 回归 (regression) 是能为一个或多个…

Linux系统的目录结构

file system hierarchy standard文件系统层级标准&#xff0c;定义了在类Unix系统中的目录结构和目录内容。 即让用户了解到已安装软件通常放置于哪个目录下。 Linux目录结构的特点 使用树形目录结构来组织和管理文件。 整个系统只有一个根目录&#xff08;树根&#xff09;&a…