【初阶数据结构】栈

目录

  • 栈的概念及结构
  • 栈的实现
    • 栈的结构
    • 栈的初始化
    • 栈的销毁
    • 入栈
    • 出栈
    • 取栈顶元素
    • 判断栈是否为空
    • 取栈中元素个数
    • 代码测试
  • 完整代码
    • Stack.h
    • Stack.c
    • test.c

请添加图片描述

栈的概念及结构

  栈:是一种特殊的线性表,它只允许在固定的一端进行插入和删除元素的操作
  栈顶:进行数据插入和删除操作的那一端。
  栈底:栈顶的另一端就是栈底。
  压栈:栈的插入数据操作就叫压栈,也称进栈,入栈,入数据在栈顶
  出栈:栈的删除数据操作叫做出栈,出数据也在栈顶
  栈不能随机访问,只能在栈顶出入数据或访问数据。
  栈数据元素遵守后进先出LIFO(Last In First Out)的原则。
在这里插入图片描述

栈的实现

  栈同样有两种存储方式,一种是链式存储,叫做链栈,一种是顺序存储,叫做顺序栈,一般情况下,用顺序栈的方式比较多。是因为用顺序表来实现对栈的操作,一方面顺序表的扩容不会像链表一样要一个个节点去malloc那么频繁,内存空间也比较大,人们也不会特别在意空间浪费的问题,并且利用顺序表尾插尾删数据的代价比链表更小;另一方面顺序表的缓存利用率高,所以我们更倾向使用顺序栈。

栈的结构

  顺序表分为了静态顺序表和动态顺序表,但是为了方便扩容,一般都采用动态顺序表,同样的,顺序栈也分为了定长的静态栈和支持动态增长的栈,我们也主要来实现动态增长的栈。
  动态增长栈的结构如下:

typedef int STDataType;
typedef struct Stack
{STDataType* a;//顺序栈,即用数组实现,a指向栈底int top;int capacity;
}ST;

这里有一个问题需要注意:
我们定义的top是什么,指向哪里,应该初始化为什么
这是有两种情况:
情况一:
top指向栈顶的那个元素:

在这里插入图片描述
由上图可知,当top指向栈顶元素时,它初始化应该为-1,而不是0,因为这样才能使得栈中有一个元素的时候,top等于0且指向那一个元素。
情况二:
top指向栈顶的下一个元素:

在这里插入图片描述
如上图,当top指向栈顶的下一个元素时,它的初始化就为0,即top可看做栈中有效元素个数。

栈的初始化

  上面我们已经说了top的两种情况,这也对应着top的两种初始化,但其实两种初始化都是正确的,看你选择哪一种,这里我们选择第二种,top为0时栈空,相当于顺序表的size。
  初始化如下:

void STInit(ST* pst)//传址调用,才能修改形参中的值,用指针接收,不再多说
{assert(pst);//传参的指针不能为空pst->a = NULL;//可以先初始化为空,后面插入数据时在添加空间pst->top = 0;pst->capacity = 0;
}

栈的销毁

void STDestory(ST* pst)
{assert(pst);free(pst->a);pst->a = NULL;pst->top = pst->capacity = 0;
}

入栈

  顺序栈只能在栈顶插入数据,其实也就等于顺序表的尾插,我们已经很清楚了,不清楚的可以再回去看看这个内容:顺序表。
  代码实现:

void STPush(ST* pst, STDataType x)
{assert(pst);if(pst->top == pst->capacity)//空间不够,要扩容{int newcapacity = pst->capacity == 0 ? 4 :2 * capacity;STDataType* tmp = (STDataType*)realloc(pst->a, newcapacity * sizeof(STDataType));if (new == NULL){perror("realloc fail");return;}pst->a = tmp;pst->capacity = newcapacity;}pst->a[pst->top] = x;pst->top++;
}

出栈

  同样的,出栈相当于顺序表的尾删,代码如下:

void STPop(ST* pst)
{assert(pst);assert(pst->top);//栈不能为空,为空则报错pst->top--;
}

取栈顶元素

  取栈顶元素也就等于读取数组中最后一个元素的值,要注意的是,下标是top还是top-1,由于我们定义的top是指向栈顶的下一个元素,所以下标应该为top-1.

STDataType STTop(ST* pst)
{assert(pst);assert(pst->top);return pst->a[pst->top-1];
}

判断栈是否为空

  判断栈是否为空,如果为空,则返回true,不为空,则返回false,此时我们用bool类型的函数去定义。我们上面在判断top的初始化时已经提到,当top == 0,则栈为空,代码如下:

bool STEmpty(ST* pst)
{assert(pst);return pst->top == 0;
} 

取栈中元素个数

  上面说到,当我们初始化top = 0时,top就相当于size,即元素个数,只要返回top即可。

int STSize(ST* pst)
{assert(pst);return pst->top;
}

代码测试

  至此,栈的有关操作就结束了,最后也不要忘了调试所写代码是否正确。这里要注意的是,在打印栈的时候,我们不能像顺序表或者链表一样写一个打印函数出来,因为链表是后进先出的,当我们需要打印这个栈时,需要一个个出栈然后打印,代码如下:

while (!STEmpty(&s))//判断栈是否为空
{printf("%d ", STTop(&s));//先读取栈顶元素STPop(&s);//后取出栈顶元素
}

测试代码如下:
在这里插入图片描述

  当然,栈也不仅仅只能打印这一种,可以有很多种情况,具体要看是如何出栈和入栈的

比如说有三个数字1,2,3,进行出栈出栈操作
则三个数既可以先全部入栈再出栈,也可以边入栈边出栈,
则这三个数出栈后全部的可能序列为:
{3,2,1}、{2,3,1}、{2,1,3}、{1,2,3}、{1,3,2}

完整代码

Stack.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>typedef int STDataType;typedef struct Stack
{STDataType* a;int top;int capacity;
}ST;//初始化和销毁
void STInit(ST* pst);
void STDestory(ST* pst);//入栈
void STPush(ST* pst, STDataType x);
//出栈
void STPop(ST* pst);//取栈顶元素
STDataType STTop(ST* pst);//判断栈是否为空
bool STEmpty(ST* pst);//获取元素个数
int STSize(ST* pst);

Stack.c

#include"Stack.h"//初始化
void STInit(ST* pst)
{pst->a = NULL;pst->capacity = 0;pst->top = 0;
}//销毁
void STDestory(ST* pst)
{assert(pst);free(pst->a);pst->a = NULL;pst->top = pst->capacity = 0;
}//入栈
void STPush(ST* pst, STDataType x)
{assert(pst);if (pst->top == pst->capacity){int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;STDataType* new = (STDataType*)realloc(pst->a, newcapacity*sizeof(STDataType));if (new == NULL){perror("realloc fail");return;}pst->a = new;pst->capacity = newcapacity;}pst->a[pst->top] = x;pst->top++;
}//出栈
void STPop(ST* pst)
{assert(pst);assert(pst->top);pst->top--;
}//取栈顶元素
STDataType STTop(ST* pst)
{assert(pst);assert(pst->top);return pst->a[pst->top - 1];
}//判断栈是否为空
bool STEmpty(ST* pst)
{assert(pst);return pst->top == 0;
}//获取元素个数
int STSize(ST* pst)
{assert(pst);return pst->top;
}

test.c

#include"Stack.h"void test()
{ST s;STInit(&s);STPush(&s, 1);STPush(&s, 2);STPush(&s, 3);STPush(&s, 4);printf("栈中元素个数为:%d\n", STSize(&s));STPop(&s);STPop(&s);printf("取出栈顶元素:%d\n", STTop(&s));printf("栈中元素个数为:%d\n", STSize(&s));printf("打印栈中元素:");while (!STEmpty(&s)){printf("%d ", STTop(&s));STPop(&s);}STDestory(&s);
}int main()
{test();return 0;
}

  今天的内容到此结束啦,感谢大家观看,如果大家喜欢,希望大家一键三连支持一下,如有表述不正确,也欢迎大家批评指正。

在这里插入图片描述

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

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

相关文章

[MDK] 介绍STM32使用C和C++混合编程的方法

目录 [MDK] 介绍STM32使用C和C混合编程的方法前言业务场景步骤1基础工程步骤2写代码步骤3添加cpp文件步骤4配置与编译上机现象后记 [MDK] 介绍STM32使用C和C混合编程的方法 前言 搞单片机编程大多数还是使用MDK编程&#xff0c;自己对MDK这个软件也比较熟悉&#xff0c;在网络…

【通信】电子科协通信专题

数字通信 最直观的通信方式-基带通信 问题&#xff1a;①无限大的带宽②天线体积

java回调机制

目录 一、简介二、示例2.1 同步回调2.2 异步回调2.3 二者区别 三、应用场景 一、简介 在Java中&#xff0c;回调是一种常见的编程模式&#xff0c;它允许一个对象将某个方法作为参数传递给另一个对象&#xff0c;以便在适当的时候调用该方法。 以类A调用类B方法为例: 在类A中…

KMP + Compose 跨平台 Android IOS 实战入门

KMP&#xff08;Kotlin Multiplatform&#xff09;是一种面向移动端开发的跨平台框架&#xff0c;使用 Kotlin 语言编写&#xff0c;可实现在 Android 和 iOS 平台上共享代码和逻辑。通过 KMP 框架&#xff0c;我们可以编写一次代码&#xff0c;然后在不同的平台上进行部署和运…

python能够干什么?

python有哪些用途&#xff1f; Python是一种高级编程语言&#xff0c;它被广泛用于各种不同的领域。以下是Python的一些常见用途&#xff1a; 网络应用开发&#xff1a;Python可以用于编写Web应用程序、API、爬虫、网络服务器等。数据科学和机器学习&#xff1a;Python拥有许…

深究muduo网络库的Buffer类!!!

最近在学习了muduo库的Buffer类&#xff0c;因为这个编程思想&#xff0c;今后在各个需要缓冲区的项目编程中都可以用到&#xff0c;所以今天来总结一下&#xff01; Buffer的数据结构 muduo的Buffer的定义如下&#xff0c;其内部是 一个 std::vector&#xff0c;且还存在两个…

Shell编程之条件语句

条件测试 文件测试与整数测试 字符串测试与逻辑测试 if语句 if单分支语句 if双分支语句 if多分支语句 case分支语句 条件测试操作 条件测试操作 1 条件判断 test命令测试表达式是否成立&#xff0c;若成立返回0.否则返回其它数值。 格式 1 test 条件表达式 格式 2 …

【Redis7】了解Redis

1.常见数据库 1.1.键值存储数据库 如 Map 一样的key-value 对&#xff0c;典型代表就是 Redis。 1.2.列存储数据库 关系型数据库是典型的行存储数据库&#xff0c;按行存储的数据在物理层面占用的是连续存储空间&#xff0c;不适合海量数据存储。而按列存储则可实现分布式存储&…

猫不爱喝水是正常的?求求別再被洗脑了!日常可以补水的主食分享

猫不爱喝水正常吗&#xff1f;看给猫喂的什么&#xff0c;喂的罐头的话不爱喝水问题不大。喂的干粮猫还长期不喝水&#xff0c;处于缺水状态&#xff0c;可能会出现便秘、上火、尿黄、尿少等症状。在高温的夏季&#xff0c;猫还可能因脱水而中暑&#xff0c;严重时甚至可能导致…

用c++实现汉诺塔问题、归并排序

6.1.3 汉诺塔问题 【问题】 汉诺塔问题(Hanio tower problem)来自一个古老的传说&#xff1a;有一座宝塔&#xff08;塔A),其上有64个金碟&#xff0c;所有碟子按从大到小由塔底堆放至塔顶。紧挨着这座宝塔有另外两座宝塔&#xff08;塔B和塔C),要求把塔A上的碟子移动到塔C上…

01-xss基本原理

核心:攻击的是前端&#xff0c; 一、课程引入 1、开发一个简单的PHP页面&#xff0c;代码如下&#xff1a; <?php // xss 基础演示代码&#xff1a;从浏览器中接受一个URL地址参数名为content if(isset($_GET[content])){$content$_GET[content];echo "你输入的内容…

再议大模型微调之Zero策略

1. 引言 尽管关于使用Deepspeed的Zero策略的博客已经满天飞了&#xff0c;特别是有许多经典的结论都已经阐述了&#xff0c;今天仍然被问到说&#xff0c;如果我只有4块40G的A100&#xff0c;能否进行全量的7B的大模型微调呢&#xff1f; 正所谓“纸上得来终觉浅&#xff0c;…

进程状态与优先级

Linux内核源代码&#xff1a; 首先我们需要明确一点&#xff0c;Linux操作系统和操作系统的进程状态是不同的 上图大概标识了各个状态对应在操作系统的状态 普通进程 R运行状态&#xff08;running&#xff09;: 并不意味着进程一定在运行中&#xff0c;它表明进程要么是在…

ROS 2边学边练(44)-- 从头开始构建一个视觉机器人模型

前言 从此篇开始我们就开始接触URDF(Unified Robot Description Format&#xff0c;统一机器人描述格式)&#xff0c;并利用其语法格式搭建我们自己的机器人模型。 动动手 开始之前我们需要确认是否安装joint_state_publisher功能包&#xff0c;如果有安装过二进制版本的urdf_…

解密某游戏的数据加密

前言 最近有个兄弟通过我的视频号加我&#xff0c;咨询能否将这个dubo游戏游戏开始前就将数据拿到从而进行押注&#xff0c;于是通过抓包工具测试了下&#xff0c;发现数据有时候是明文&#xff0c;有时候确实密文&#xff0c;大致看了下有这几种加密&#xff1a;Md5aes、Md5&a…

网络层协议之 IP 协议

IP 协议格式 4 位版本&#xff1a;此处的取值只有两个&#xff0c;4&#xff08;IPv4&#xff09;和 6&#xff08;IPv6&#xff09;&#xff0c;即指定 IP 协议的版本。 4 位首部长度&#xff1a;描述了 IP 报头多长&#xff0c;IP 报头是变长的&#xff0c;因为报头中的选项部…

点餐小程序 点餐系统 微信点餐系统 支持微信小程序 支付公众号 可接入第三方配送 全开源uniapp

餐饮连锁v2版-体验后台&#xff08;复制粘贴以下地址到浏览器&#xff0c;打开网址即可登录&#xff0c;) 本文来自&#xff1a;点餐小程序 点餐系统 微信点餐系统 支持微信小程序 支付公众号 可接入第三方配送 全开源uniapp - 源码1688 演示后台&#xff1a;https://diancan.…

异构图神经网络——Heterogeneous Graph Neural Networks

相关代码见文末 1.回顾同构图 1.1 GNN GNN基本计算方法——邻接矩阵乘以节点,聚合相邻节点的特征,得到本节点的特征表达 1.2 Graph Attention Network 引入图注意力,实现边的权重可学习,最简单的方法是,将两个节点的特征进行拼接,使用一组可学习的权重参数映射为边的权…

华为数据之道第一部分导读

目录 导读 第一部分 序 第1章 数据驱动的企业数字化转型 非数字原生企业的数字化转型挑战 业态特征&#xff1a;产业链条长、多业态并存 运营环境&#xff1a;数据交互和共享风险高 IT建设过程&#xff1a;数据复杂、历史包袱重 数据质量&#xff1a;数据可信和一致化…

学习大数据,所需更要的shell基础(2)

文章目录 read读取控制台输入函数系统函数bashnamedirname 自定义函数Shell工具&#xff08;重点&#xff09;cutawk 正则表达式入门常规匹配常用特殊字符 read读取控制台输入 1&#xff09;基本语法 read (选项) (参数) ①选项&#xff1a; -p&#xff1a;指定读取值时的提示…