当前位置: 首页 > news >正文

深入解析数据结构之栈及其应用

目录

  • 一、栈
    • 1.1 概念与结构
    • 1.2 栈的实现
      • 1.2.1 初始化
      • 1.2.2 销毁
      • 1.2.3 入栈
      • 1.2.4 判断栈是否为空
      • 1.2.5 出栈
      • 1.2.6 取栈顶元素
      • 1.2.7 获取栈中有效元素个数
  • 二、完整源码
  • 三、栈相关算法题
  • 总结


一、栈

1.1 概念与结构

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

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶
出栈:栈的删除操作叫做出栈,出数据也在栈顶
在这里插入图片描述

栈底层结构选型
栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。原因如下:

使用数组来实现栈时,栈的核心操作(入栈,出栈)只需操作数组尾部元素,时间复杂度都是O(1)

struct Stack
{int* arr;int size;int capacity;
}

使用链表来实现栈时,栈的核心操作(入栈,出栈)都可以在链表的头部进行操作,时间复杂度都是O(1)

struct Stack
{int data;struct Stack* next;
}

这样看好像二者没有差别,但数组的优势不止如此:

  1. 数组的元素在内存中连续存储,所以其访问元素通过索引直接定位,无需像链表那样遍历查找
  2. 数组无需存储额外的指针/引用信息,内存利用率更高。链表每个节点需要额外空间存储指针,尤其在元素数量较多时,额外开销显著。(就比如说这里要存储3个整型数据,用链表来实现栈的空间更大,因为用链表实现要有3个节点大小的空间,一个节点差不多8个字节,消耗的空间更大。数组实现的话,就是向操作系统申请几个整型空间,也就是4个字节4个字节的增加,消耗空间更小)
  3. 数组实现只需要维护一个顶部指针,逻辑简单。链表实现需要处理节点的创建、删除和指针维护,容易出错。

可以发现栈的物理结构取决于底层实现,如果用数组实现,底层就是连续的(线性的),用链表实现(不连续的)

1.2 栈的实现

1.2.1 初始化

函数声明:
在这里插入图片描述
这里虽然栈的结构和顺序表几乎一摸一样,但是不同的数据结构,其提供的方法是不一样的。栈只有一端开口,栈只能在栈顶入数据和出数据

函数定义:
在这里插入图片描述
测试文件:
在这里插入图片描述

1.2.2 销毁

//销毁
void STDesTroy(ST* ps);

在这里插入图片描述

1.2.3 入栈

//入栈---栈顶
void STPush(ST* ps, STDataType x);

栈顶在数组的尾部,接下来把size改为top更符合栈的结构,top指向栈顶的位置,刚好就是栈中有效数据个数
在这里插入图片描述
这里和顺序表类似,空间足够,插入一个数据,size++

空间不够的话,就需要先额外增容了
在这里插入图片描述
在这里插入图片描述
补充:ps不能为空,否则对空指针进行解引用了
在这里插入图片描述
在这里插入图片描述

1.2.4 判断栈是否为空

出栈之前有个操作是判断栈是否为空,栈为空无数据可出了

//判断栈是否为空
bool STEmpty(ST* ps);

栈顶为0的时候,说明栈中的有效数据个数为0
在这里插入图片描述

1.2.5 出栈

//出栈---栈顶
void STPop(ST* ps);

此时有5个空间,栈顶在下一个要插入数据的位置,栈顶对应的下标为4
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
出栈即少一个数据,栈顶要减减,此时栈中有4个有效的空间,只有3个有效的数据(3个有效的数据就是1,2,3,不包含4)

1.2.6 取栈顶元素

//取栈顶元素
STDataType STTop(ST* ps);

取栈顶并不是出栈,而是把栈顶这个元素返回
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这个操作叫循环出栈,输出结果体现出栈后进先出的规则

1.2.7 获取栈中有效元素个数

//获取栈中有效元素个数
int STSize(ST* ps);

在这里插入图片描述
在这里插入图片描述


二、完整源码

Stack.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>//定义栈的结构 - Stack是栈的名称
typedef int STDataType;
typedef struct Stack {STDataType* arr;//存储数据的数组int top;//top指向栈顶的位置,刚好就是栈中有效数据个数int capacity;//栈的空间大小
}ST;//初始化
void STInit(ST* ps);
//销毁
void STDesTroy(ST* ps);//入栈---栈顶
void STPush(ST* ps, STDataType x);
//出栈---栈顶
void STPop(ST* ps);
//取栈顶元素
STDataType STTop(ST* ps);//判断栈是否为空
bool STEmpty(ST* ps);
//获取栈中有效元素个数
int STSize(ST* ps);

Stack.c

#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"//初始化
void STInit(ST* ps)
{ps->arr = NULL;ps->top = ps->capacity = 0;
}//销毁
void STDesTroy(ST* ps)
{//arr不能为空,为空的话都没有向操作系统中申请内存,就没有必要销毁了if (ps->arr)free(ps->arr);ps->arr = NULL;ps->capacity = ps->top = 0;
}//入栈---栈顶
void STPush(ST* ps, STDataType x)
{assert(ps);//判断空间是否足够if (ps->top == ps->capacity){//增容int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//如果capacity为0,就给4,realloc第二个参数的单位为字节,这里要申请4个整型空间,而不是4个字节STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc fail!");exit(1);}ps->arr = tmp;ps->capacity = newCapacity;}//空间足够ps->arr[ps->top++] = x;
}//判断栈是否为空
bool STEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}//出栈---栈顶
void STPop(ST* ps)
{assert(!STEmpty(ps));ps->top--;
}//取栈顶元素
STDataType STTop(ST* ps)
{assert(!STEmpty(ps));return ps->arr[ps->top - 1];
}//获取栈中有效元素个数
int STSize(ST* ps)
{assert(ps);return ps->top;
}

test.c

#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"void test01()
{ST st;STInit(&st);STPush(&st, 1);STPush(&st, 2);STPush(&st, 3);STPush(&st, 4);printf("size:%d\n", STSize(&st));//STPop(&st);while (!STEmpty(&st)){//取栈顶STDataType top = STTop(&st);printf("%d ", top);//出栈STPop(&st);}printf("\n");STDesTroy(&st);
}int main()
{test01();return 0;
}

三、栈相关算法题

有效的括号
在这里插入图片描述
在这里插入图片描述

思路:借助数据结构—栈,遍历字符串,左括号入栈,是右括号就取栈顶元素比较,是否匹配

在这里插入图片描述
第一种情况,匹配的话,就让左括号出栈,出栈之后,继续在字符串中遍历
在这里插入图片描述
这里用一个指针pi来遍历字符串,此时拿到一个右大括号,此时取栈顶取到的是一个左大括号,将其与* pi比较,是匹配的让左大括号出去

以此类推,循环往复,当pi遍历完字符串,此时栈为空,说明这就是一个有效的字符串
在这里插入图片描述

这里参数有一个char* s,s就是字符串,s指向字符串的第一个字符,循环截至的条件是遍历完字符串
基于上述思路代码如下:
在这里插入图片描述
在这里插入图片描述

这里看似写完了,实则不然,对比案例测试一下,能否覆盖所有情况
在这里插入图片描述
这里只有左括号,入栈之后pi++,此时pi走到\0,跳出while循环,就直接销毁了,所以这里没有写全
在这里插入图片描述
这样代码看着有点冗余,这里可以写为三目表达式
此时再测试分析
在这里插入图片描述
第一个是右括号,直接进else取栈顶,但是当前栈为空,所以还需要特殊处理,调用STTop会出现断言报错
在这里插入图片描述

在这里插入图片描述
此情况也推理符合
最后完整代码如下:
在这里插入图片描述


总结

以上就是深入解析数据结构之栈,下一篇内容为栈和队列,喜欢的兄弟们不要忘记一键三连支持一下


文章转载自:

http://g40Hjl57.wfbmr.cn
http://j83qp6qD.wfbmr.cn
http://dKdJ4VFc.wfbmr.cn
http://oGIUr1Zk.wfbmr.cn
http://XPiKK20N.wfbmr.cn
http://teDgxATm.wfbmr.cn
http://vDP9zg3v.wfbmr.cn
http://9eYjIxjX.wfbmr.cn
http://DaydxySM.wfbmr.cn
http://zvIPhz27.wfbmr.cn
http://ceL9AkXK.wfbmr.cn
http://tfbMjNYN.wfbmr.cn
http://dtZF1N3O.wfbmr.cn
http://EKJlKl7O.wfbmr.cn
http://F9XxRsAf.wfbmr.cn
http://bM0kPey1.wfbmr.cn
http://jPP1omeV.wfbmr.cn
http://l6rYlxil.wfbmr.cn
http://j3B4we0z.wfbmr.cn
http://H6SzCOTv.wfbmr.cn
http://3HY4UIQG.wfbmr.cn
http://ao8wy5zU.wfbmr.cn
http://3XsXbKD9.wfbmr.cn
http://hVs5ZyP7.wfbmr.cn
http://mXN9Ot3Q.wfbmr.cn
http://1bkc8U4E.wfbmr.cn
http://3VhJ9Yhy.wfbmr.cn
http://Q7MdhzZR.wfbmr.cn
http://5hs0HnXc.wfbmr.cn
http://8bG2wkBE.wfbmr.cn
http://www.dtcms.com/a/379125.html

相关文章:

  • (一)昇腾AI处理器技术
  • BUUCTF刷题十一道(14)
  • Linux防火墙-Iptables
  • python访问基于docker搭建的elasticsearch
  • logback-spring.xml文件说明
  • 【PyTorch训练】为什么要有 loss.backward() 和 optimizer.step()?
  • 抖音大数据开发一面(0905)
  • 原生js的轮播图
  • 连接池项目考点
  • ruoyi-flowable-plus框架节点表单的理解
  • js.228汇总区间
  • BERT中文预训练模型介绍
  • 光平面标定建立激光点与世界坐标的对应关系
  • Jmeter执行数据库操作
  • 基于FPGA的图像中值滤波算法Verilog开发与开发板硬件测试
  • 微软Aurora大模型实战:五大数据源驱动、可视化对比与应用
  • 【论文笔记】SpaRC: Sparse Radar-Camera Fusion for 3D Object Detection
  • C++基本数据类型的范围
  • Spring AI(三)多模态支持(豆包)
  • agentic Deep search相关内容补充
  • 第一篇:如何在数组中操作数据【数据结构入门】
  • PYcharm——pyqt音乐播放器
  • OpenAI已正式开放ChatGPT Projects
  • 日系电车销量破万,真正突围了,恰恰说明了电车的组装本质!
  • Linux 防火墙 Iptables
  • 不想考地信,计算机又太卷,所以转型GIS开发
  • PotPlayer 1.7.22611发布:支持蓝光播放+智能字幕匹配
  • LVS负载均衡群集与Keepalived高可用
  • React中hook的用法及例子(持续更新)
  • 【网络编程】TCP、UDP、KCP、QUIC 全面解析