【脚踢数据结构】深入理解栈

  • (꒪ꇴ꒪ ),Hello我是祐言QAQ
  • 我的博客主页:C/C++语言,Linux基础,ARM开发板,软件配置等领域博主🌍
  • 快上🚘,一起学习,让我们成为一个强大的攻城狮!
  • 送给自己和读者的一句鸡汤🤔:集中起来的意志可以击穿顽石!
  • 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏

一、什么是栈?

        栈是一种重要的数据结构。它是一种特殊的线性表,特殊在只允许在表的一端进行插入和删除操作,这一端被称为栈顶,相对的,另一端被称为栈底。在这篇博客中,我们将详细地介绍栈的概念,包括顺序栈链栈的实现。   

         栈是一种线性数据结构,它遵循 "先入后出"(Last-In-First-Out,LIFO)的原则。这意味着最后插入栈的元素将首先被移除,而最早插入的元素将最后被移除。就像如果1先进入了栈底,你想再拿到它,那么就要先把 6 5 4 3 2依次拿出来才能拿到 1。

 二、顺序栈

        顺序栈是栈的一种实现方式,它是用一组连续的存储单元存储栈中的元素,并需要一个栈顶指针 *top指出栈顶元素,初始化为-1,栈的大小 size则决定了最多可以放多少数据。

                                                                    顺序栈结构体

1.顺序栈的管理结构体

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>typedef int Datatype;//顺序栈结构体
typedef struct Sequent_stack
{Datatype *stack;		//存放数据的位置int size;				//栈的大小int top;				//栈顶偏移
}S_stack;

2.初始化

//初始化顺序栈
S_stack *init_stack(int size)
{S_stack *s1= malloc(sizeof(S_stack));if (s1 != NULL){s1->stack = calloc(size, sizeof(Datatype));s1->size = size;s1->top = -1;}return s1;
}


3.判断栈空栈满

//是否栈空
bool isempty(S_stack *s)
{return (s->top == -1);
}//是否栈满
bool isfull(S_stack *s)
{return (s->top == s->size-1);
}

4.压栈(入栈)

//压栈
bool push(S_stack *s, Datatype data)
{if (isfull(s)){return false;}//将栈顶往上偏移一个s->top++;//将数据,放到栈顶指向的位置s->stack[s->top] = data;  //*(s->stack+s->top) = data;return true;
}

5.弹栈(出栈)

//弹栈
bool pop(S_stack *s, Datatype *data)
{if (isempty(s)){return false;}//先拿到栈顶指向的数据*data = s->stack[s->top];//data = *(s->stack+s->top);//栈顶在往下偏移一个s->top--;return true;
}

6.遍历顺序栈

//遍历栈
void display(S_stack *s)
{for (int i = 0; i <= s->top; ++i){printf("%d ", s->stack[i]);}printf("\n");
}

练习:

        输入一个大于零的十进制数,转化为八进制,转化过程中使用顺序栈结构来实现。输入: 123  输出 :转化为八进制是 0173

         完整代码:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>typedef int Datatype;//顺序栈结构体
typedef struct Secqune_stack
{Datatype *stack;		//存放数据的位置int size;				//栈的大小int top;				//栈顶偏移
}S_stack;//初始化顺序栈
S_stack *init_stack(int size)
{S_stack *s1= malloc(sizeof(S_stack));if (s1 != NULL){s1->stack = calloc(size, sizeof(Datatype));s1->size = size;s1->top = -1;}return s1;
}//栈空
bool isempty(S_stack *s)
{return (s->top == -1);
}//栈满
bool isfull(S_stack *s)
{return (s->top == s->size-1);
}//压栈(入栈)
bool push(S_stack *s, Datatype data)
{if (isfull(s)){return false;}//将栈顶往上偏移一个s->top++;//将数据,放到栈顶指向的位置s->stack[s->top] = data;  //*(s->stack+s->top) = data;return true;
}//弹栈(出栈)
bool pop(S_stack *s, Datatype *data)
{if (isempty(s)){return false;}//先拿到栈顶指向的数据*data = s->stack[s->top];//data = *(s->stack+s->top);//栈顶在往下偏移一个s->top--;return true;
}int main(int argc, char const *argv[])
{//初始化顺序栈S_stack *s = init_stack(22);int num;scanf("%d", &num);while(num){push(s, num%8);num/=8;}printf("十进制数转为八进制数等于:0");int data;while(!isempty(s)){pop(s, &data);printf("%d", data);}printf("\n");return 0;
}

三、链式栈

        链式栈是栈的另一种常见实现方式,它通过链表来表示栈的结构。链式栈相对于顺序栈的一个主要优势是它可以动态地调整大小,适用于栈容量需求不确定或需要频繁的插入和删除操作的情况。与之相对应的是链式栈也需要更多的内存空间用于存储节点指针。

        在具体的指针操作方面与单项链表相似。

1.链式栈的管理结构体


typedef int Datatype;typedef struct Node
{Datatype data;			//数据struct Node *prev;		//指向上一个节点
}node;//链式栈的节点
typedef struct List_stack
{node *stack;		//每一个节点的数据int size;			//长度
}Lstack;

2.初始化

//初始化链式栈
Lstack *init_list_stack()
{Lstack *s = malloc(sizeof(Lstack));if (s!=NULL){s->stack = NULL;s->size = 0;}return s;
}

3.判断栈空

        由于链式栈独特的结构使之不会出现栈满的情况,而是根据需要随时动态调整栈容量,因此我们无需判断链式栈是否栈满。

//链式栈的栈空判断
bool isempty(Lstack *s)
{return (s->size == 0);
}

4.压栈

//压栈(入栈)
bool push(Lstack *s, Datatype data)
{node *new = malloc(sizeof(node));if (new != NULL){//如果new不为空new->data = data;//新节点的prev指向栈原来的尾部new->prev = s->stack;//新节点现在是不是就变为现在链式栈的尾部s->stack = new;//栈的大小+1s->size++;return true;}else{return false;}
}

5.弹栈

//弹栈(出栈)
bool pop(Lstack *s, Datatype *data)
{//栈空if (isempty(s)){return false;}//先将栈的尾部(栈顶)的节点的地址拿到*data = s->stack->data;//将尾部(栈顶)的节点往前挪一个s->stack = s->stack->prev;//链式栈的节点个数-1s->size--;return true;
}

练习:

        输入一个大于零的十进制数,转化为十六进制,转化过程中使用链式栈结构来实现。输入: 123  输出 :转化为八进制是 0x7B  

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>typedef int Datatype;typedef struct Node
{Datatype data;			//数据struct Node *prev;		//指向上一个节点
}node;//链式栈的节点
typedef struct List_stack
{node *stack;		//每一个节点的数据int size;			//长度
}Lstack;//初始化链式栈
Lstack *init_list_stack()
{Lstack *s = malloc(sizeof(Lstack));if (s!=NULL){s->stack = NULL;s->size = 0;}return s;
}//链式栈的栈空判断
bool isempty(Lstack *s)
{return (s->size == 0);
}//压栈(入栈)
bool push(Lstack *s, Datatype data)
{node *new = malloc(sizeof(node));if (new != NULL){//如果new不为空new->data = data;//新节点的prev指向栈原来的尾部new->prev = s->stack;//新节点现在是不是就变为现在链式栈的尾部s->stack = new;//栈的大小+1s->size++;return true;}else{return false;}
}//弹栈(出栈)
bool pop(Lstack *s, Datatype *data)
{//栈空if (isempty(s)){return false;}//先将栈的尾部(栈顶)的节点的地址拿到*data = s->stack->data;//将尾部(栈顶)的节点往前挪一个s->stack = s->stack->prev;//链式栈的节点个数-1s->size--;return true;
}int main(int argc, char const *argv[]) {Lstack *s = init_list_stack();int num;Datatype data;char hexDigits[] = "0123456789ABCDEF";  // 十六进制字符表示printf("请输入一个十进制数:");scanf("%d", &num);while (num > 0) {int num1 = num % 16;push(s, num1);num /= 16;}printf("转化为十六进制是:0x");while (!isempty(s)) {pop(s, &data);printf("%c", hexDigits[data]);}printf("\n");// 释放内存while (pop(s, &data)) {free(s->stack);s->stack = s->stack->prev;}free(s);return 0;
}

四、顺序栈和链栈的比较


        顺序栈和链栈是栈的两种不同实现方式,它们各有优点和缺点。

        1.空间分配:顺序栈需要一次性分配一整块连续的内存空间,可能导致空间浪费。而链栈则可以动态分配空间,用多少分配多少,因此在内存利用上更有优势。

        2.时间复杂度:由于顺序栈是基于数组的,因此在访问元素时可以直接通过索引进行访问,时间复杂度为O(1)。而链栈需要通过指针进行遍历,时间复杂度为O(n)。

        3.插入和删除:两者在插入和删除操作上都是O(1)的时间复杂度。顺序栈通过改变栈顶指针位置进行插入和删除,链栈通过改变指针的指向进行插入和删除。

五、栈的应用


        栈在计算机科学和日常编程中扮演着重要的角色,以下是一些常见的使用场景:

        1.函数调用:每当程序调用一个函数时,系统会将返回地址和必要的信息存入系统栈中,待函数运行完毕后再从栈中恢复这些信息。

        2.表达式求值:栈可以用于算术或逻辑表达式的求值。例如,计算机程序通常使用两个栈来处理数学表达式,一个栈用于存储操作数,另一个栈用于存储运算符。

        3.括号匹配:栈可以用于检查一段文本中的括号是否正确匹配。当遇到一个开括号时,将其压入栈中。当遇到一个闭括号时,从栈顶弹出一个开括号,如果能够匹配,则继续处理,否则就是不匹配。

        总的来说,栈是一种非常实用的数据结构,理解和掌握栈以及其操作对于深入理解计算机程序的运行机制以及编写高效的代码有着重要的作用。

        另外留一个括号匹配的题,代码会放评论区。

 

六、总结

        学习数据结构是一个漫长的过程,但只要我们持续不断地学习和实践,就一定能够掌握它。希望这篇文章能帮助你对栈有更深的理解和应用。如果你有任何问题或者想要讨论的点,欢迎在下面的评论区留言。

       

        更多C语言Linux系统ARM板实战数据结构相关文章,关注专栏:

   手撕C语言

            玩转linux

                    脚踢数据结构

                            6818(ARM)开发板实战

📢写在最后

  • 今天的分享就到这啦~
  • 觉得博主写的还不错的烦劳 一键三连喔~
  • 🎉感谢关注🎉

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

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

相关文章

数据清理在数据科学中的重要性

什么是数据清理&#xff1f; 推荐&#xff1a;使用 NSDT场景编辑器 助你快速搭建可编辑的3D应用场景 在数据科学中&#xff0c;数据清理是识别不正确数据并修复错误的过程&#xff0c;以便最终数据集可供使用。错误可能包括重复字段、格式不正确、字段不完整、数据不相关或不准…

web集群学习:源码安装nginx配置启动服务脚本、IP、端口、域名的虚拟主机

目录 1、源码安装nginx&#xff0c;并提供服务脚本。 2、配置基于ip地址的虚拟主机 3、配置基于端口的虚拟主机 4、配置基于域名的虚拟主机 1、源码安装nginx&#xff0c;并提供服务脚本。 1、源码安装会有一些软件依赖 &#xff08;1&#xff09;检查并安装 Nginx 基础依赖…

数据结构刷题训练——链表篇(二)

目录 前言 1.题目一&#xff1a;链表分割 1.1 思路 1.2 分析 1.3 题解 2. 题目二&#xff1a;相交链表 2.1 思路 2.2 分析 2.3 题解 3. 题目三&#xff1a;环形链表 3.1 思路 3.2 分析 3.3 题解 总结 前言 本期继续分享链表相关的OJ题目&#xff0c;在这个专栏博客…

【论文阅读】EULER:通过可扩展时间链接预测检测网络横向移动(NDSS-2022)

作者&#xff1a;乔治华盛顿大学-Isaiah J. King、H. Howie Huang 引用&#xff1a;King I J, Huang H H. Euler: Detecting Network Lateral Movement via Scalable Temporal Graph Link Prediction [C]. Proceedings 2022 Network and Distributed System Security Symposium…

UDP通信实验、广播与组播、本地套接字

文章目录 流程函数应用广播应用 组播&#xff08;多播&#xff09;本地套接字应用 流程 函数 返回值&#xff1a; 成功&#xff0c;返回成功发送的数据长度 失败&#xff0c;-1 返回值&#xff1a; 成功&#xff0c;返回成功接收数据长度 失败&#xff0c;-1 应用 广播 应用 …

MongoDB文档-进阶使用-MongoDB索引-createindex()与dropindex()-在MongoDB中使用正则表达式来查找

阿丹&#xff1a; 之前研究了MongoDB的基础增删改查。在学会基础的数据库增删改查肯定是不够的。这个时候就涉及到了数据库搜索的时候的效率。需要提高数据的搜索效率。 MongoDB索引 在所以数据库中如果没有数据索引的时候。如果需要查找到一些数据。都会去主动扫描所有可能存…

mybatis-plus的逻辑删除的坑

一旦在逻辑字段上加了TableLogic逻辑删除的配置&#xff0c;并且使用mybatis-plus自带的方法时&#xff08;如果自己用xml写SQL不会出现下面的情况&#xff09; 查询、修改时会自动排除逻辑删除的数据 当使用mybatis-plus自带的查询方法时&#xff0c;就不用每次查询的时候跟…

Blazor 简单组件(1):B_Icon开发

文章目录 前言ICON开发使用 前言 Blazor 简单组件(0)&#xff1a;简单介绍 ICON开发 <i class"Type" style"font-size:(Size)px;color:Color;"></i>code {/// <summary>/// icon类型/// </summary>[Parameter]public string Typ…

nginx 以及nginx优化

目录 nginx功能介绍 静态文件服务 反向代理 动态内容处理 SSL/TLS 加密支持 虚拟主机支持 URL 重写和重定向 缓存机制 日志记录 可扩展性和灵活性 nginx的主要应用场景 nginx常用命令 nginx另外一种安装方式 nginx常用的信号符&#xff1a; nginx配置文件详解 n…

Nginx复现

docker复现Nginx配置漏洞 2.1CRLF(carriage return/line feed)注入漏洞 这个漏洞产生的原因是请求重定向的错误配置&#xff0c;导致在url中输入回车换行符可以控制http响应头部 比如&#xff1a;location / { return 302 https://$host$uri; } 原本的目的是为了让http的…

【JavaEE基础学习打卡02】是时候了解JavaEE了

目录 前言一、为什么要学习JavaEE二、JavaEE规范介绍1.什么是规范&#xff1f;2.什么是JavaEE规范&#xff1f;3.JavaEE版本 三、JavaEE应用程序模型1.模型前置说明2.模型具体说明 总结 前言 &#x1f4dc; 本系列教程适用于JavaWeb初学者、爱好者&#xff0c;小白白。我们的天…

c#在设计时调试自定义 Windows 窗体控件

private string demoStringValue null; [Browsable(true)] public string DemoString {get{return this.demoStringValue;}set{demoStringValue value;} } 参考链接 在设计时调试自定义控件 - Windows Forms .NET Framework | Microsoft Learnhttps://learn.microsoft.com/z…

生信豆芽菜——配对型的复杂箱线图使用说明

网站&#xff1a;http://www.sxdyc.com/visualsBoxHalfPlot 一、配对型的复杂箱线图简介 配对型的复杂箱线图原理与箱线图相同&#xff0c;常见于配对样本的数据分析中&#xff0c;在日常研究中&#xff0c;我们会碰到配对资料&#xff0c;例如同一病人治疗前后的变化&#xff…

数据结构: 线性表(带头双向循环链表实现)

之前一章学习了单链表的相关操作, 但是单链表的限制却很多, 比如不能倒序扫描链表, 解决方法是在数据结构上附加一个域, 使它包含指向前一个单元的指针即可. 那么怎么定义数据结构呢? 首先我们先了解以下链表的分类 1. 链表的分类 链表的结构非常多样, 以下情况组合起来就有…

LVS集群

目录 1、lvs简介&#xff1a; 2、lvs架构图&#xff1a; 3、 lvs的工作模式&#xff1a; 1&#xff09; VS/NAT&#xff1a; 即&#xff08;Virtual Server via Network Address Translation&#xff09; 2&#xff09;VS/TUN &#xff1a;即&#xff08;Virtual Server v…

7.2 手撕VGG11模型 使用Fashion_mnist数据训练VGG

VGG首先引入块的思想将模型通用模板化 VGG模型的特点 与AlexNet&#xff0c;LeNet一样&#xff0c;VGG网络可以分为两部分&#xff0c;第一部分主要由卷积层和汇聚层组成&#xff0c;第二部分由全连接层组成。 VGG有5个卷积块&#xff0c;前两个块包含一个卷积层&#xff0c…

MySQL_SQL性能分析

SQL执行频次 语法&#xff1a; SHOW GLOBAL STATUS LIKE COM_类型; COM_insert; 插入次数 com_delete; 删除次数 com_update; 更新次数 com_select; 查询次数 com_______; 注意&#xff1a;通过语法&#xff0c;可以查询到数据库的实际状态&#xff0c;就可以知道数据库是以增删…

TDesign中后台管理系统-用户登录

目录 1 创建用户表2 开发后端接口3 测试接口4 修改登录页面调用后端接口最终效果总结 中后台系统第一个要实现的功能就是登录了&#xff0c;我们通常的逻辑是让用户在登录页面输入用户名和密码&#xff0c;调用后端接口去验证用户的合法性&#xff0c;然后根据接口返回的结果进…

【T3】金蝶kis凭证数据转换到畅捷通T3软件中。

【问题需求】 将金蝶软件中的账套转换到畅捷通T3软件中。 由于金蝶老版本使用的是非sql server数据库。 进而需要将其数据导入到sql中,在转换到T3。 【转换环境】 金蝶中数据:凭证;科目无项目核算。 1、金蝶的数据文件后缀为.AIS; 2、安装office2003全版软件; 3、安装sq…

【算法】双指针——leetcode盛最多水的容器、剑指Offer57和为s的两个数字

盛水最多的容器 &#xff08;1&#xff09;暴力解法 算法思路&#xff1a;我们枚举出所有的容器大小&#xff0c;取最大值即可。 容器容积的计算方式&#xff1a; 设两指针 i , j &#xff0c;分别指向水槽板的最左端以及最右端&#xff0c;此时容器的宽度为 j - i 。由于容器…