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

news2025/2/23 23:32:30
  • (꒪ꇴ꒪ ),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;
		//栈的大小+1
		s->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;
	//链式栈的节点个数-1
	s->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;
		//栈的大小+1
		s->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;
	//链式栈的节点个数-1
	s->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.coloradmin.cn/o/863516.html

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

相关文章

哨兵2号在SNAP中去云处理后

1.Fmask软件需要对1C级产品进行处理 软件下载,链接到github地址 我下载的是4.5版本&#xff0c;无脑安装即可。 双击打开软件&#xff08;需要等一会&#xff09;&#xff0c;长这样 路径选择E:\S2\S2A_MSIL1C_20220806T032531_N0400_R018_T49RBQ_20220806T054540\S2A_MSIL1…

每天一道leetcode:剑指 Offer 12. 矩阵中的路径(中等DFS深度优先遍历)

今日份题目&#xff1a; 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻的单元格内的字母构成&#xff0c;其中“相邻”单元…

一文讲述什么是数字孪生?

当前世界正处于百年未有之大变局&#xff0c;数字经济在各国已成为经济发展的重点。数字经济也是我国社会经济发展的必经之路。 近些年&#xff0c;大数据、人工智能、数字孪生等技术的发展促使技术与国内各产业进一步融合&#xff0c;从而推动了各产业在智能化、数字化等方面…

雷军写的代码上热搜了!

就在昨天&#xff0c;「雷军写的代码」相关话题先后上了一波热搜和热榜。 出于好奇&#xff0c;第一时间点进去围观了一波。 原来雷总马上要在8月14日举办他的2023年度演讲了&#xff0c;并且也放出了对应的演讲海报。 这个海报可以说暗藏玄机&#xff0c;放大后仔细一看&#…

前端部署流程详解

部署流程 1.打包前端项目成一个dist文件夹 命令: npm run build作用&#xff1a;用vue-cli内部集成的webpack&#xff0c;把 .vue, .less, .js 等打包成浏览器可直接执行的代码 html&#xff0c;css&#xff0c;js。 结果&#xff1a;会在项目根目录下创建 /dist目录&…

UEFI+win7+多系统安装

物理主机先安装的Windows10&#xff0c;同时需要安装Windows7的双系统 1.在https://next.itellyou.cn/下载Windows 7 ISO 2.使用Rufus制作U盘安装盘 注意一定要选择FAT32格式&#xff0c;否则安装过程会卡住 3.由于官方纯净的安装镜像默认不支持UEFI安装&#xff0c;有两种解决…

github的issue最大支持的字符数为65536 characters

Issue templates and forms are currently unavailable. Please try again later. Comment is too long (maximum is 65536 characters) There was an error creating your Issue: body is too long (maximum is 65536 characters). 所以不能存储内容过大的markdown笔记&…

互联网医院|线上医疗平台如何建设运营服务商?

互联网医院平台紧密结合了大数据和人工智能技术&#xff0c;为医疗服务提供了更精准和个性化的解决方案。通过对海量的医疗数据进行分析和挖掘&#xff0c;平台能够为医生提供更多准确可靠的参考依据&#xff0c;辅助医生做出更好的诊断和治疗决策。 在选择互联网医院建设运营服…

Spring kafka源码分析——消息是如何消费的

文章目录 概要端点注册创建监听容器启动监听容器消息拉取与消费小结 概要 本文主要从Spring Kafka的源码来分析&#xff0c;消费端消费流程&#xff1b;从spring容器启动到消息被拉取下来&#xff0c;再到执行客户端自定义的消费逻辑&#xff0c;大致概括为以下4个部分&#x…

直播|深入解析 StarRocks 存算分离—云原生湖仓 Meetup#2

StarRocks 3.0 正式开启极速统一的湖仓新范式&#xff0c;借助云原生存算分离构架、极速数据湖分析、物化视图等重量级特性实现湖仓架构升级&#xff0c;兼具数据仓库查询高性能与数据湖低成本可扩展的优势&#xff0c;让用户更简单地实现极速统一的湖仓分析。 作为 StarRocks…

05-bean工厂的准备工作

入口方法 prepareBeanFactory(beanFactory);protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {// Tell the internal bean factory to use the contexts class loader etc.// 设置beanFactory的classloader为当前context的classloaderbeanF…

计算机组成原理-笔记-第六章

目录 六、第六章——总线 1、总线&#xff08;基本概念&#xff09; &#xff08;1&#xff09;总线的定义 & 特性 &#xff08;2&#xff09;串行 &并行 &#xff08;3&#xff09;总线的分类 &#xff08;4&#xff09;总线分类——功能 &#xff08;4.1&…

分布式数据库视角下的存储过程

存储过程很好呀&#xff0c;那些用不好的人就是自己水平烂&#xff0c;不接受反驳&#xff01;我就有过这样念头&#xff0c;但分布式数据库&#xff0c;更倾向少用或不用存储过程。 1 我从C/S时代走来 C/S架构时代的末期最流行开发套件是PowerBuilder和Sybase数据库&#xf…

性能测试最佳实践的思考,7个要点缺一不可!

性能测试是软件开发和应用过程中至关重要的环节。它是评估系统性能、稳定性和可扩展性的有效手段&#xff0c;可以确保软件在真实环境中高效运行。在现代技术快速发展的时代&#xff0c;性能测试的重要性愈发显著。 性能测试在软件开发和应用过程中的重要性不可低估。它是保障…

程序员怎么利用ChatGPT解放双手=摸鱼?

目录 1. 当你遇到问题时为你生成代码ChatGPT 最明显的用途是根据查询编写代码。我们都会遇到不知道如何完成任务的情况&#xff0c;而这正是人工智能可以派上用场的时候。例如&#xff0c;假设我不知道如何使用 Python 编写 IP 修改器&#xff0c;只需查询 AI&#xff0c;它就…

模拟实现string类

string类的接口有很多&#xff0c;这里我来梳理一下自己觉得有意思的几个&#xff0c;并模拟实现一下可以凑合用的my_string&#xff0c;话不多说直接开干&#xff1a; 注意事项 为了和库里的string冲突&#xff0c;所以就将自己实现的my_string放在一个命名空间里 namespace …

采用pycharm在虚拟环境使用pyinstaller打包python程序

一年多以前&#xff0c;我写过一篇博客描述了如何虚拟环境打包&#xff0c;这一次有所不同&#xff0c;直接用IDE pycharm构成虚拟环境并运行pyinstaller打包 之前的博文&#xff1a; 虚拟环境venu使用pyinstaller打包python程序_伊玛目的门徒的博客-CSDN博客 第一步&#xf…

明天就要去面试软件测试岗了,现在我能怎么做呢?

首先&#xff0c;时间已经不允许你进行大面积的专业复习&#xff0c;所以你应该做好能够立竿见影的准备工作&#xff1a; 1、整理好自己的仪表 先去理个发&#xff0c;让自己看起来精神一点&#xff0c;尤其是男生&#xff0c;整理头发&#xff0c;修修鬓角能够快速让人对自己…

安卓13不再支持PPTP怎么办?新的连接解决方案分享

随着Android 13的发布&#xff0c;我们迎来了一个令人兴奋的新品时刻。然而&#xff0c;对于一些用户而言&#xff0c;这也意味着必须面对一个重要的问题&#xff1a;Android 13不再支持PPTP协议。如果你是一个习惯使用PPTP协议来连接换地址的用户&#xff0c;那么你可能需要重…

机器学习实战——波士顿房价预测

波士顿房价预测 波士顿房地产市场竞争激烈&#xff0c;而你想成为该地区最好的房地产经纪人。为了更好地与同行竞争&#xff0c;你决定运用机器学习的一些基本概念&#xff0c;帮助客户为自己的房产定下最佳售价。幸运的是&#xff0c;你找到了波士顿房价的数据集&#xff0c;…