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

  • (꒪ꇴ꒪ ),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.rhkb.cn/news/86767.html

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

相关文章

【BI系统】选型常见问题解答二

本文主要总结BI系统选型过程中遇见的常见问题&#xff0c;并针对性做出回答&#xff0c;希望能为即将选型&#xff0c;或正在选型BI系统的企业用户们提供一个快速了解通道。 有针对金蝶云星空的BI方案吗&#xff1f;能起到怎样的作用&#xff1f; 答&#xff1a;奥威BI系统拥…

小内存嵌入式设备软件的差分升级设计(学习)

摘要 提出一种改进HDiffPatch算法并在复旦微单片机上实现小内存差分升级的方案&#xff0c;即使用单片机内的Flash空间替代算法占用的RAM空间&#xff0c;从而减少算法对单片机RAM空间的需求&#xff0c;以满足小内存微处理器的差分升级&#xff0c;同时对算法内存分配释放函数…

关于pycharm安装出现的interpreter field is empty,无法创建项目存储位置

关于pycharm安装出现的interpreter field is empty&#xff08;解释器为空&#xff09; 关于pycharm安装出现的interpreter field is empty&#xff0c;无法创建项目存储的位置。如图&#xff1a; 我之前安装的时候一直老是有这个提示&#xff0c;后来才发现是因为没安装这个p…

干货满满的Python知识,学会这些你也能成为大牛

目录 1. 爬取网站数据 2. 数据清洗与处理 3. 数据可视化 4. 机器学习模型训练 5. 深度学习模型训练 6. 总结 1. 爬取网站数据 在我们的Python中呢&#xff0c;使用爬虫可以轻松地获取网站的数据。可以使用urllib、requests、BeautifulSoup等库进行数据爬取和处理。以下是…

案例12 Spring MVC入门案例

网页输入http://localhost:8080/hello&#xff0c;浏览器展示“Hello Spring MVC”。 1. 创建项目 选择Maven快速构建web项目&#xff0c;项目名称为case12-springmvc01。 2.配置Maven依赖 <?xml version"1.0" encoding"UTF-8"?><project xm…

nacos原理

不要纠结于具体代码&#xff0c;随着版本变化源码多变&#xff0c;要学习的是基本原理和思想&#xff1b; Nacos注册中心实现原理分析 Nacos架构图 其中分为这么几个模块&#xff1a; Provider APP&#xff1a;服务提供者。 Consumer APP&#xff1a;服务消费者。 Name Serv…

k8s之StorageClass(NFS)

一、前言 1、环境 k8s v1.23.5 &#xff0c;服务器是centos7.9 192.168.164.20 k8s-master1 192.168.164.30 k8s-node1 192.168.164.40 k8s-node2 2、貌似storageClass在kubernetes v1.20就被砍了。 因为它比较慢&#xff0c;而且耗资源&#xff0c;但可以通过不同的实现镜…

Java并发机制的底层实现原理

一、前置知识 缓存一致性协议&#xff1a;每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了&#xff0c;当处理器发现自己缓存行对应的内存地址被修改&#xff0c;就会将当前处理器的缓存行设置成无效状态&#xff0c;当处理器对这个数据进行修改操作的时…

selenium常见等待机制及其特点和使用方法

目录 1、强制等待 2、隐式等待 3、显示等待 1、强制等待 强制等待是在程序中直接调用Thread.sleep(timeout) ,来完成的&#xff0c;该用法的优点是使用起来方便&#xff0c;语法也比较简单&#xff0c;缺点就是需要强制等待固定的时间&#xff0c;可能会造成测试的时间过…

“探索计算机世界:进程的基本概念与功能“

文章目录 前言什么是进程如何描述进程进程的属性1. 进程标识符2. 内存指针3. 文件描述符表4. 进程的状态5. 优先级6. 上下文7. 记账信息 内存分配并行和并发 前言 作为程序员&#xff0c;理解计算机的组成以及计算机是怎样运行的是很重要的&#xff0c;因为只有了解计算机我们…

Jenkins 使用

Jenkins 使用 文章目录 Jenkins 使用一、jenkins 任务执行二、 Jenkins 连接gitee三、Jenkins 部署静态网站 一、jenkins 任务执行 jenkins 创建 job job的名字最好是有意义的 restart_web_backend restart_web_mysql[rootjenkins ~]# ls /var/lib/jenkins/ config.xml …

QT--崩溃原因分析

本文为学习记录&#xff0c;若有错误&#xff0c;请联系作者&#xff0c;谦虚受教。 文章目录 前言一、目的二、实现步骤1 add2line.exe2 分析文件3 crash文件 三、相关代码1 pro文件2.ccrashstack.h3.ccrashstack.cpp4.main.cpp 总结 前言 你从来来去自由&#xff0c;若你不想…

大模型时代,如何重塑AI人才的培养?知名高校专家为您解答

当下&#xff0c;随着人工智能技术的快速发展&#xff0c;大模型已经成为了人工智能发展的新方向&#xff0c;同时也对新时代AI人才的需求和培养带来了新的思考与挑战&#xff0c;需要结合当下社会对复合型AI人才的需求进行新思考&#xff0c;创新AI人才培养模式&#xff0c;以…

【ARM64 常见汇编指令学习 15 -- ARM 标志位的学习】

文章目录 ARM 标志位介绍Zero Condition flag(零标志位)零标志位判断实例 上篇文章&#xff1a;ARM64 常见汇编指令学习 14 – ARM 汇编 .balign,.balignw,.balign 伪指令学习 下篇文章&#xff1a;ARM64 常见汇编指令学习 16 – ARM64 SMC 指令 ARM 标志位介绍 在ARM架构中&am…

深度对话|如何设计合适的网络经济激励措施

近日&#xff0c;我们与Mysten Labs的首席经济学家Alonso de Gortari进行了对话&#xff0c;讨论了如何在网络运营商和参与者之间找到激励措施的平衡&#xff0c;以及Sui的经济如何不断发展。 是什么让您选择将自己的经济学背景应用于区块链和Web3领域&#xff1f; 起初&…

YOLO相关原理(文件结构、视频检测等)

超参数进化(hyperparameter evolution) 超参数进化是一种使用了genetic algorithm&#xff08;GA&#xff09;遗传算法进行超参数优化的一种方法。 YOLOv5的文件结构 images文件夹内的文件和labels中的文件存在一一对应关系 激活函数&#xff1a;非线性处理单元 activation f…

爬虫014_文件操作_打开关闭_读写_序列化_反序列化---python工作笔记033

报错,没有指定路径,没有指定路径无法创建文件 这样可以在当前目录下创建一个可写的文件 可以看到找到刚才生成的文件,看看内容

探讨uniapp的navigator 页面跳转问题

navigator 页面跳转。该组件类似HTML中的<a>组件&#xff0c;但只能跳转本地页面。目标页面必须在pages.json中注册。 "tabBar": {"color": "#7A7E83","selectedColor": "#3cc51f","borderStyle": "bl…

SpringMVC的架构有什么优势?——控制器(三)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…

走进知识图谱(二)【世界知识图谱篇】知识表示的经典模型与平移模型及基于复杂关系建模的知识表示学习

上篇文章提到&#xff0c;该系列文章将主要围绕世界知识图谱和语言知识图谱这两大类知识图谱进行展开&#xff0c;并且提到知识图谱的主要研究包括了知识表示学习、知识自动获取和知识的推理与应用三大部分。今天主要介绍世界知识图谱的知识表示学习&#xff0c;其中包括经典的…