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

  • (꒪ꇴ꒪ ),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)开发板实战

📢写在最后

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

群晖7.X版安装cpolar内网穿透

群晖7.X版安装cpolar内网穿透套件 文章目录 群晖7.X版安装cpolar内网穿透套件前言1. 下载cpolar的群晖系统套件1.1 在“套件中心” 选择“手动安装”1.2 完成套件安装 2. 进入cpolar软件信息页3. 点击“免费注册”轻松获得cpolar账号 前言 随着群晖系统的更新换代&#xff0c;…

taro Swiper组件--异形滚动

效果 <SwiperindicatorDots{false}previousMargin50pxnextMargin50pxautoplay{false}interval100onChange{onChangeSwiper} >{[1,2,3].map((item, index) > {return (<SwiperItemkey{item-${index}}><View className{demo-item ${currentIndex index ? ac…

功能上新|全新GPU性能优化方案

GPU优化迎来了全新的里程碑&#xff01;我们深知移动游戏对高品质画面的追求日益升温&#xff0c;因此UWA一直着眼于移动设备GPU性能优化&#xff0c;以确保您的游戏体验尽善尽美。然而&#xff0c;不同GPU芯片之间的性能差异及可能导致的GPU瓶颈问题&#xff0c;让优化工作变得…

sentinel---滑动窗口的实现原理

sentinel有多种规则&#xff0c;包括&#xff1a;降级、限流、热点等等规则&#xff0c;这些规则均会涉及到时间因素&#xff0c;既在单位时间内的请求量满足各种条件之后的各种动作。 这里我们一起来探针一下sentinel中滑动窗口的实现 如上是一个滑动窗口的示意图。 这里先不…

内生安全构建数据存储

一、数据安全成为防护核心&#xff0c;存储安全防护不容有失 1、数据作为企业的核心资产亟需重点保护&#xff0c;数据安全已成网络空间防护核心 2、国家高度重视关键信息基础设施的数据安全&#xff0c;存储安全已成为审核重点 二、存储安全是数据安全的关键一环&#xff0c;应…

腾讯云CVM服务器竞价实例是什么?和按量计费有什么区别?

腾讯云服务器CVM计费模式分为包年包月、按量计费和竞价实例&#xff0c;什么是竞价实例&#xff1f;竞价实例和按量付费相类似&#xff0c;优势是价格更划算&#xff0c;缺点是云服务器实例有被自动释放风险&#xff0c;腾讯云服务器网来详细说下什么是竞价实例&#xff1f;以及…

Electron + Vue3 + Vite + TS 构建桌面应用

之前是使用React、Electron、TS和webpack来构建桌面应用的。虽然功能齐全,但是打包等等开发的体验不太理想,总感觉太慢了。作为一个开发者,我们总是希望,执行构建命令后,可以快速打包或者启动本地应用,且通过更少的配置,来完成开发体验。 现在的vite已经得到广泛的应用…

16-3_Qt 5.9 C++开发指南_使用QStyle 设置界面外观_实现不同系统下的界面效果的匹配

文章目录 1. QStyle的作用&#xff08;实现不同系统下的界面效果的匹配&#xff09;2. Qt内置样式的使用3. 源码3.1 可视化UI设计3.2 mainwindow.cpp 1. QStyle的作用&#xff08;实现不同系统下的界面效果的匹配&#xff09; Qt 是一个跨平台的类库&#xff0c;相同的界面组件…

Kafka:springboot集成kafka收发消息

kafka环境搭建参考Kafka&#xff1a;安装和配置_moreCalm的博客-CSDN博客 1、springboot中引入kafka依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><…

机器学习深度学习——RNN的从零开始实现与简洁实现

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——循环神经网络RNN &#x1f4da;订阅专栏&#xff1a;机器学习&&深度学习 希望文章对你们有所帮…

TSINGSEE青犀视频安防监控视频平台EasyCVR设备在线,视频无法播放的原因排查

可支持国标GB28181、RTMP、RTSP/Onvif、海康Ehome、海康SDK、大华SDK、宇视SDK等多种协议接入的安防监控视频平台EasyCVR基于云边端一体化架构&#xff0c;具有强大的数据接入、处理及分发能力&#xff0c;可在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、…

28岁,从字节退休了···

大厂一直是每个程序员都向往职业目标&#xff0c;大厂意味着薪资高、福利好、倍有面儿&#xff0c;而且发展空间也大。甚至有人调侃不想进大厂的程序员不是好程序员。 而在网上&#xff0c;也有各个网友分享自己在大厂的经历&#xff0c;在某平台还有一个近2600万浏览的话题&a…

Nginx与Tomcat的区别,什么是HTTP服务器(处理静态资源的服务器),什么是处理动态资源的服务器

Nginx和Tomcat都是常用的Web服务器&#xff0c;但它们的主要作用不同。Nginx是一个HTTP服务器&#xff0c;反向代理服务器和通用TCP/UDP代理服务器。它通常用于静态内容、媒体流和负载均衡。在高流量和高并发负载下&#xff0c;Nginx表现更出色&#xff0c;并且能够轻松处理静态…

STM32入门——DMA数据搬运工

DMA简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输&#xff0c;无须CPU干预&#xff0c;节省了CPU的资源12个独立可配置的通道&#xff1a; DMA1&#xff08;7个通道&#xff09;&#xff…

从零开始学python(十六)爬虫集群部署

前言 今天讲述Python框架源码专题最后一个部分&#xff0c;爬虫集群部署&#xff0c;前面更新了十五个从零开始学python的系列文章&#xff0c;分别是&#xff1a; 1.编程语法必修篇 2.网络编程篇 3.多线程/多进程/协程篇 4.MySQL数据库篇 5.Redis数据库篇 6.MongoDB数据库篇 …

postgresql之内存池-GenerationContext

创建GenerationContext MemoryContext GenerationContextCreate(MemoryContext parent,const char *name,Size blockSize) {GenerationContext *set; ...set (GenerationContext *) malloc(MAXALIGN(sizeof(GenerationContext))); .../* Fill in GenerationContext-specific …

个保新标 | 《信息安全技术 敏感个人信息处理安全要求》(征求意见稿)发布

8 月 9 日&#xff0c;全国信息安全标准化技术委员会公开发布关于国家标准《信息安全技术 敏感个人信息处理安全要求》&#xff08;征求意见稿&#xff09;&#xff08;以下简称《标准》&#xff09;的通知&#xff0c;面向社会广泛征求意见。 《标准》的制定背景是为支撑《个人…

vscode连接远程Linux服务器

文章目录 一、环境安装1.1 下载vscode1.2 下载vscode-sever 二、ssh链接2.1 安装Remote-SSH2.2 设置vscode ssh2.3 设置免密登录2.3.1 本地生成公私钥2.3.2 服务器端添加公钥 三、安装插件3.1 vscode安装插件3.1.1 在线安装插件3.1.2.1 下载插件3.1.2.2 安装插件 3.2 vscode-se…

【Flutter】【packages】simple_animations 简单的实现动画

package&#xff1a;simple_animations 导入包到项目中去 可以实现简单的动画&#xff0c; 快速实现&#xff0c;不需要自己过多的设置 有多种样式可以实现[ ] 功能&#xff1a; 简单的用例&#xff1a;具体需要详细可以去 pub 链接地址 1. PlayAnimationBuilder PlayAnima…

Dockerfile部署golang,docker-compose

使用go镜像打包&#xff0c;运行在容器内 redis和mysql用外部的 项目目录结构 w1go项目&#xff1a; Dockerfile # 这种方式是docker项目加上 本地的mysql和redis环境 # go打包的容器 FROM golang:alpine AS builder# 为我们镜像设置一些必要的环境变量 ENV GO111MODULEon …