数据结构深入理解--栈
目录
一、栈的定义
二、栈的实现
2.1 栈的结构
2.2 栈的初始化
2.3 栈的销毁
2.3 栈元素的插入
2.4 栈元素的删除
2.5 栈顶元素获取
2.6 栈元素有效个数获取
2.7 栈是否为空判断
三、代码总览
Stack.h
Stack.c
测试代码:test.c
四、例题
例一:
例二:
例三:
一、栈的定义
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除 操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out) 的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
栈可以这样理解:相信大家都对枪械有一定粗略的了解,咱们就用压子弹来帮助大家进行理解。
压栈,大家可以想象为压子弹,子弹是一发一发往下压,那压栈就是在容量之内一个数据一个数据往下压。
出栈,大家可以理解为子弹射出的过程,即:最后压入的子弹先出。数据最后的先出。
这就是压栈与出栈的过程,当然也可以压一个出一个,压多个出一个都可以,大家完全就可以把栈当作压子弹和子弹射出。
好了,基础知识我们已经掌握,那么,我们该用什么结构来实现栈呢?
通过单链表,双链表还是什么?大家可以在此处进行思考,稍后公布答案。
二、栈的实现
上文说到,我们要选择一个结构来实现栈。我们来一一分析一下:
双链表全称为:带头双向循环链表,用它来实现可以吗?还用说吗?太过于完美当然可以,但是要用两个指针,同学们,一个指针已经困扰大家已久,那两个指针想必是大家不想经历的大恐怖。所以,这个时候咱们把它先列为备胎(实在没办法在想它(在特殊情况下能渣则渣)🐶)。
单链表全称为:不带头单向不循环链表,大家想单链表找到尾元素麻烦吗?找一遍时间复杂度为O(N)。虽然我们可以反转一下,但是你愿意用吗?要是放两个指针还不如用双链表。
这个时候怎么办?难道我们要使用双链表?不,绝对不行。这时,数组意外路过,对啊,我们可以用数组。
数组每次使用前像顺序表一样判断是否开辟空间,在用一个变量size来记录尾,这样不就完美符合要求了。那说干就干吧。打开我们心爱的VS。
2.1 栈的结构
栈的结构,可以借鉴一下顺序表,要有数组、容量和栈定元素。结构如下:
// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{STDataType* a;int top; // 栈顶int capacity; // 容量
}Stack;
2.2 栈的初始化
要进行初始化,大家想一想top赋值为多大合适,如果为0,那么0是不是为栈的第一个元素?是不是?答案是:是的。那么,有没有办法不叫top指向数组第一个元素?有,使top的值为-1即可。在后续代码中,我会将top初始化为0(别问,问就是top此时可以当顺序表中的size使用)。代码如下:
// 初始化栈
void StackInit(Stack* ps)
{assert(ps);ps->a = NULL;ps->capacity = ps->top = 0;
}
2.3 栈的销毁
在我们今后写代码一定要记住:只要你malloc,realloc一定要free,你创建了就一定要销毁。
那我们创建了一个栈,那么我们一定要销毁。代码如下:
// 销毁栈
void StackDestroy(Stack* ps)
{assert(ps);free(ps->a);//此处记得释放ps指向的数组,不要写成ps!!!ps->a = NULL;ps->capacity = ps->top = 0;
}
注意事项写在代码里了,一定要记住!!!
2.3 栈元素的插入
void StackPush(Stack* ps, STDataType data)
{assert(ps);if (ps->capacity == ps->top){int newcapacity = ps->capacity == 0 ? 4 : 2 * sizeof(ps->capacity);STDataType* newnode = (STDataType*)realloc(ps->a,newcapacity*sizeof(STDataType));if (newnode == NULL){perror("realloc fail");return;}ps->capacity = newcapacity;ps->a = newnode;}//这里之所以没有封装成一个接口,是因为这里只用一次,其余的都不使用ps->a[ps->top] = data;ps->top++;//这里也可以合二为一//ps->a[ps->top++] = data;
}
此处要点与顺序表类似,就不过多强调。
2.4 栈元素的删除
// 出栈
void StackPop(Stack* ps)
{assert(ps);assert(ps->top > 0);ps->top--;
}
此处代码过于简单,那么能不能不把这个封装了,直接写。其实你要是想这么干,你可以试一试,不过提醒一下:可能会出乱子。还是那句话:专业的事专业的人做。
2.5 栈顶元素获取
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{assert(ps);assert(ps->top > 0);return ps->a[ps->top - 1];
}
对于此代码最后的返回值可能会有人有疑问,我简单解释一下:
2.6 栈元素有效个数获取
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{assert(ps);return ps->top;
}
上文说过top初始化为0时可当size来使用,代码简单,不过多解释。
2.7 栈是否为空判断
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
bool StackEmpty(Stack* ps)
{assert(ps);return ps->top == 0;
}
注意点:必须包含头文件:stdbool.h。
三、代码总览
Stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>// 支持动态增长的栈
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
bool StackEmpty(Stack* ps);
// 销毁栈
void StackDestroy(Stack* ps);
Stack.c
#include"Stack.h"// 初始化栈
void StackInit(Stack* ps)
{assert(ps);ps->a = NULL;ps->capacity = ps->top = 0;
}
// 入栈
void StackPush(Stack* ps, STDataType data)
{assert(ps);if (ps->capacity == ps->top){int newcapacity = ps->capacity == 0 ? 4 : 2 * sizeof(ps->capacity);STDataType* newnode = (STDataType*)realloc(ps->a,newcapacity*sizeof(STDataType));if (newnode == NULL){perror("realloc fail");return;}ps->capacity = newcapacity;ps->a = newnode;}//这里之所以没有封装成一个接口,是因为这里只用一次,其余的都不使用ps->a[ps->top] = data;ps->top++;//这里也可以合二为一//ps->a[ps->top++] = data;
}
// 出栈
void StackPop(Stack* ps)
{assert(ps);assert(ps->top > 0);ps->top--;
}
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{assert(ps);assert(ps->top > 0);return ps->a[ps->top - 1];
}
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{assert(ps);return ps->top;
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
bool StackEmpty(Stack* ps)
{assert(ps);return ps->top == 0;
}
// 销毁栈
void StackDestroy(Stack* ps)
{assert(ps);free(ps->a);//此处记得释放ps指向的数组,不要写成ps!!!ps->a = NULL;ps->capacity = ps->top = 0;
}
测试代码:test.c
#include"Stack.h"int main()
{Stack p;StackInit(&p);StackPush(&p, 1);StackPush(&p, 2);StackPush(&p, 3);StackPush(&p, 4);while (!StackEmpty(&p)){printf("%d ", StackTop(&p));StackPop(&p);}StackDestroy(&p);return 0;
}
四、例题
既然明白了,那么来几道题巩固一下吧!
例一:
设栈S和队列 Q的初始状态均为空,元素 abcdepg 依次进入栈S。若每个元素出栈后立即进入队列 Q,且7个元素出队的顺序是 bdcfeag,则栈S的容量至少是:
例二:
若元素a,b,c,d,e,f依次进栈,允许进栈、退栈操作交替进行,但不允许连续3次进行退栈操作,不可能得到的出栈序列是()。
A. dcebfa B. cbdaef C.bcaefd D.afedcb
例三:
元素 a,b,c,d,e依次进入初始为空的栈中,若元素进栈后可停留、可出栈,直到所有元素都出栈,则在所有可能的出栈序列中,以元素d开头的序列个数是
好了,我们的学习到现在就结束了,如有疑惑可私信,也可在评论区留言。
完!
相关文章:

数据结构深入理解--栈
目录 一、栈的定义 二、栈的实现 2.1 栈的结构 2.2 栈的初始化 2.3 栈的销毁 2.3 栈元素的插入 2.4 栈元素的删除 2.5 栈顶元素获取 2.6 栈元素有效个数获取 2.7 栈是否为空判断 三、代码总览 Stack.h Stack.c 测试代码:test.c 四、例题 例一: 例二ÿ…...

Maven 的仓库、周期和插件
优质博文:IT-BLOG-CN 一、Maven 仓库 在Maven的世界中,任何一个依赖、插件或者项目构建的输出,都可以称为构建。Maven在某个统一的位置存储所有项目的共享的构建,这个统一的位置,我们就称之为仓库。任何的构建都有唯一…...

parallels desktop19最新免费Mac电脑虚拟机软件
Parallels Desktop是一款运行在Mac电脑上的虚拟机软件,它允许用户在Mac系统上同时运行多个操作系统,比如Windows、Linux等。通过这款软件,Mac用户可以轻松地在同一台电脑上体验不同操作系统的功能和应用程序,而无需额外的硬件设备…...

波动性悖论:为何低风险股票长期跑赢高风险对手?
从去年开始,“红利低波”类的产品净值稳步向上,不断新高,让很多人关注到了A股“分红高”、“波动率低”这两类股票。分红高的公司更受投资者青睐,这从基本面的角度很容易理解,那么波动率低的股票明明波动更小ÿ…...
环信设置头像昵称(安卓android)版
在此真的要吐槽吐槽环信,那么大的公司,文档那么乱。。。真的像一坨屎一样,翻个demo东翻西翻,官网论坛看的眼瞎。。。几乎要放弃了,还好百度到别人的看了看弄出来了 1、首先,要确认自己用的是哪个环信的UI库…...
Rust:用 Warp 库实现 Restful API 的简单示例
直接上代码: 1、源文件 Cargo.toml [package] name "xcalc" version "0.1.0" edition "2021"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies] warp "…...

【SpringBoot】 什么是springboot(一)?如何搭建springboot项目?
文章目录 SpringBoot第一章1、什么是springboot1、回顾ssm项目搭建流程2、springboot项目的优点2、搭建springboot项目方式1:方式2:第二章1、基本配置1、热部署2、注解3、端口配置application.properties特点application.yml特点注意4、环境配置springboot中的配置文件要求5、…...

从loss角度理解LLM涌现能力
如今的很多研究都表明小模型也能出现涌现能力,本文的作者团队通过大量实验发现模型的涌现能力与模型大小、训练计算量无关,只与预训练loss相关。 作者团队惊奇地发现,不管任何下游任务,不管模型大小,模型出现涌现能力…...
debian apt 更改阿里源
1. 备份文件 cp /etc/apt/sources.list /etc/apt/sources.list.bak 2. 更改 sources.list文件内容为: deb http://mirrors.aliyun.com/debian/ buster main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster main non-free contrib deb htt…...

Spring Cloud | “微服务“ 架构 与 Spring Cloud
“微服务” 架构 与 Spring Cloud 目录: "微服务" 架构 与 Spring Cloud1. 认识架构"单体" 架构"SOA" 架构"微服务" 架构 2. "微服务架构" 的功能 :① 微服务架构的 "自动化部署"② 服务 "集中化管理"③…...

win10禁止自动更新的终极方法
添加注册表值 1.运行,输入regedit 2.打开注册表编辑器依次进入以下路径“计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings”。 3.在Settings项中,新建DWORD(32位)值(D),重命名为以下命名“Fl…...

笨方法自学python(二)-注释
注释和#号 程序里的注释是很重要的。它们可以用自然语言告诉你某段代码的功能是什么。在你想要临时移除一段代码时,你还可以用注解的方式将这段代码临时禁用。 # A comment, this is so you can read your program later. # Anything after the # is ignored by py…...

wireshark的安装使用及相关UDP、TCP、 ARP
初步了解: 进入wireshark后如图: 从图中可以看到很多网络连接在操作的时候我们需要监测哪些 我们可以直接在本地的运行框中输入ipconfig来查看 如图: 从以上图片中我们可以清楚地看到哪些网络连接已经连接的我们只需要按需监测他们即可 但…...

【软考】模拟考卷错题本2024-05-11
1 设计模式- 适配器模式 基本上上述的图解已经涵盖了绝大多数主流的设计模式和其特点。理解记忆下即可,这里对下午的考题也有帮助的。 2 计算机组成原理 cpu 访问速度 这个真的是憨憨咯~看到内存就选内存,题目都没审好。这里的速度比cpu内部的要比外部的…...

VMware虚拟机提示内存不足
VMware虚拟机,k8s集群搭建内存不足的问题 疑问:我的电脑是8G8G双通道的内存,当我在搭建k8s集群时给master-2G内存,node1-3G内存,node2-3G内存; 当依次打开虚拟机到node2时VM提示“物理内存不足,…...

视频批量剪辑指南:一键合并视频并添加背景音乐,高效便捷
在数字化时代,视频剪辑已经成为了一项常见且重要的技能。无论是制作家庭影片、工作展示还是社交媒体内容,掌握高效的视频剪辑技巧都能极大地提升我们的工作效率和创作质量。本文将为您介绍云炫AI智剪中高效的视频批量剪辑方法,让您能够一键合…...
讲讲C++四种类型转换
在C中,类型转换(或称为类型转换运算符)是用来将一个数据类型转换为另一个数据类型的机制。C提供了四种类型转换:静态类型转换(Static Cast)、动态类型转换(Dynamic Cast)、重新解释类…...

探索LLM在广告领域的应用——大语言模型的新商业模式和新个性化广告的潜力
概述 在网络搜索引擎的领域中,广告不仅仅是一个补充元素,而是构成了数字体验的核心部分。随着互联网经济的蓬勃发展,广告市场的规模已经达到了数万亿美元,并且还在持续扩张。广告的经济价值不断上升,它已经成为支撑大…...

MBR与GPT分区表
文章目录 MBR分区表MBR分区表结构MBR分区表项查看U盘的分区表信息查看系统中所有磁盘的分区类型获取分区表信息 GPT分区表保护性MBRGPT分区表头格式GPT分区表项格式分区类型分区属性分区表项内容 MBR分区表 CHS :磁头(Heads)、柱面(Cylinder…...

OGG几何内核开发-BRepAlgoAPI_Fuse与BRep_Builder.MakeCompound比较
最近在与同事讨论BRepAlgoAPI_Fuse与BRep_Builder.MakeCompound有什么区别。 一、从直觉上来说,BRepAlgoAPI_Fuse会对两个实体相交处理,相交的部分会重新的生成相关的曲面。而BRep_Builder.MakeCompound仅仅是把两个实体组合成一个新的实体,…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...

如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...