栈和队列及其多种接口实现-c语言

news/2024/5/15 23:31:42/文章来源:https://blog.csdn.net/KLZUQ/article/details/128037639

今天我们来完成栈和队列,首先我们要明白什么是栈,什么是队列。

目录

栈的选择

栈的结构

栈的初始化

栈的销毁

入栈

出栈

返回栈顶元素

计算数据个数

判断是否为空

队列的选择

队列的结构

入队列

出队列

判断是否为空

取队头元素

取队尾元素

计算数据个数

队列的销毁


我们之前写过顺序表,链表,这都是数据结构,而栈和队列,也是数据结构,栈的特殊地方在于,栈是后进先出,也叫LIFO(last in first out),比如我们平时在桌子上堆放的书,我们堆放了十本书,如果不直接把书抽出来的话,我们要拿到最下面那本书,需要把上面九本先拿下去,也就是先要从最上面的那一本开始拿,而最上面的那一本,是我们最后放上去的,这就是一个栈。再说队列,队列的特殊地方在于先进先出,也叫FIFO(first in first out),比如我们日常生活里的排队就是一个队列,不考虑插队这些,正常情况下,第一个来的人一定第一个完成自己的业务,最后一个来的人最后完成,这就是一个队列。

我们还是采取工程化的写法。

 首先我们来完成栈,栈的实现可以使用数组或者链表,那我们选取哪个比较好呢?

栈的选择

我们来比较一下二者的优缺点。

 

首先我们看数组: 使用数组就相当于之前顺序表的尾插和尾删,用尾去做栈顶,非常合适,唯一的缺点是空间不足时需要扩容。完美避开了顺序表插入删除时需要挪动数据的缺点。

我们再来看链表,链表又有多种结构,我们介绍一种使用单链表来完成的,插入和删除都只有O(1)。

使用这种结构即可完成,最初栈顶和栈底都为空,插入第一个元素后,栈顶栈底都指向第一个元素,每次插入新元素后,只需将其赋给栈顶,删除时,先删除节点,再把栈顶指向栈顶的next即可。

如果用尾做栈顶,那么使用双向链表最好,如果使用头做栈顶,那么单链表最好,插入删除都是O(1)。

知道了两种结构完成栈的优缺点,大家会选择哪一个呢?我们这里选择数组来完成栈,因为使用数组的话各方面效率都会更优,这里知识和预加载有关,这里就不多介绍,我们举个例子,使用数组和链表的区别,学生时代家里每个月都会给你打生活费,数组就像是一个月给你打一次生活费,够你一个月花,链表就像每天给你打一次,很明显,使用数组要比链表效率高。

决定好了用什么来实现栈,接着我们来完成栈的结构和各类接口

栈的结构

typedef int STDataType;typedef struct Stack {STDataType* a;int top;int capacity;
}ST;

我们将其写为动态栈,即a不直接写为数组,不然就写死了,并且我们使用top来记录栈顶位置

接着我们来完成栈的初始化

栈的初始化

void StackInit(ST* ps)//初始化
{assert(ps);ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);if (ps->a == NULL) {printf("malloc fail\n");exit(-1);}ps->capacity = 4;ps->top = 0;
}

这里的top选取0还是-1看自己喜欢,选取-1代表的是top就是当前栈顶元素,选取0表示top为栈顶元素的后一位,我这里选择初始赋0,同时,如果初始化失败,我们输出提示并结束程序

栈的销毁

void StackDestory(ST* ps)//销毁
{assert(ps);free(ps->a);ps->a = NULL;ps->top = ps->capacity = 0;
}

入栈

void StackPush(ST* ps, STDataType x)//入栈
{assert(ps);if (ps->top == ps->capacity) {//满了扩容STDataType* tmp= (STDataType*)realloc(ps->a,ps->capacity*2*sizeof(STDataType));if (tmp == NULL) {printf("realloc fail\n");exit(-1);}else {ps->a = tmp;ps->capacity *= 2;}}ps->a[ps->top] = x;ps->top++;
}

入栈时我们要判断栈是否满了,满了需要扩容,扩容时我们扩容到原来的二倍,并且要判断是否扩容失败,失败的话我们给出提示并且结束程序,入栈后要记得给top+1

出栈

void StackPop(ST* ps)//出栈
{assert(ps);assert(ps->top>0);ps->top--;
}

出栈我们直接使top-1,有人可能会先让栈顶元素置为0,但是这句是不需要的,因为入栈元素有时候本身就可能为0,并且我们不能随意出栈,比如top为0时,即空栈,所以我们加一句断言

返回栈顶元素

STDataType StackTop(ST* ps)//返回栈顶元素
{assert(ps);assert(ps->top > 0);return ps->a[ps->top - 1];
}

和出栈同理,栈为空时不能返回,我们加一句断言

计算数据个数

void StackSize(ST* ps)//计算数据个数
{assert(ps);return ps->top;
}

判断是否为空

bool StackEmpty(ST* ps)//判断是否为空
{assert(ps);return ps->top == 0;
}

我们直接return ps->top==0,使用bool类型,如果top==0,为真,返回true,否则返回false

我们来测试一下我们的代码

可以看到,没有问题。

 完成了栈,我们接着来看队列

队列的选择

和栈一样,队列也需要在数组和链表里选择一个,队列只允许在它的一端插入数据,在另一端删除数据

 这次其实我们要选择链表,因为数组会出现假溢出现象,比如这个数组可以放5个元素,现在已经放了四个,我们出队列一个元素,再入队列一个,就会发现数组下标0的位置是空的,但队列却显示已满,如果要挪动数据的话也非常麻烦,还有一种办法就是写成循环队列,但那是后续的事情,而且也有局限性。选取链表的话,用单链表就可以解决,采用上图结构,队列就只需要尾插和头删,效率都很高。

选择好了用什么来实现队列后,我们来设计队列的结构吧

队列的结构

typedef int QDataType;typedef struct QueueNode
{struct QueueNode* next;QDataType data;
}QNode;typedef struct Queue
{QNode* head;QNode* tail;
}Queue;

我们设计好队列的节点,然后采取两个指针,队头和队尾来设计队列即可。而且采用这样的结构可以避免使用二级指针(后续接口如果参数传QNode的话是需要二级指针的)

入队列

void QueuePush(Queue* pq, QDataType x)//入队列
{assert(pq);QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL) {printf("malloc fail\n");exit(-1);}newnode->data = x;newnode->next = NULL;if (pq->tail == NULL) {pq->head = pq->tail = newnode;}else {pq->tail->next = newnode;pq->tail = newnode;}
}

入队列是在队尾入元素,入队列我们需要判断队列是否为空,入队列需要一个新节点,我们给节点申请一个空间,申请失败我们输出提示并且报错,然后将x赋给节点的data,节点的next置为空,我们还要判断队列是否有节点,如果一个没有,那么队头和队尾都指向这个新节点,否则队尾的next指向新节点,再把队尾指向新节点

出队列

void QueuePop(Queue* pq)//出队列
{assert(pq);assert(pq->head);if (pq->head->next == NULL) {free(pq->head);pq->head = pq->tail = NULL;}else {Queue* next = pq->head->next;free(pq->head);pq->head = next;}
}

出队列是出队头元素,我们要进行断言,我们出队列需要把头节点的空间释放,然后让head指向它的next,但在释放前我们需要把head的next保存起来,否则我们就找不到next了,这是正常情况,如果队列只有一个元素,出队列后会造成tail的野指针现象,所以我们需要额外判断,所以我们把出队列分为队列有一个元素和多个元素的情况

判断是否为空

bool QueueEmpty(Queue* pq)//判断是否为空
{assert(pq);return pq->head == NULL;
}

我们直接返回该判断语句即可,为空返回true,否则返回false

取队头元素

QDataType QueueFront(Queue* pq)//取队头元素
{assert(pq);assert(pq->head);return pq->head->data;
}

我们直接返回队头元素即可,最好再加一句断言,有时候会很有用,比如初始化后,队列里没有元素,这种情况没有第二句断言我们就不知道程序哪里出现了错误

取队尾元素

QDataType QueueBack(Queue* pq)//取队尾元素
{assert(pq);assert(pq->head);return pq->tail->data;
}

取队尾元素与取队头元素同理

计算数据个数

int QueueSize(Queue* pq)//计算数据个数
{assert(pq);int size = 0;QNode* cur = pq->head;while (cur) {cur = cur->next;size++;}return size;
}

这个接口有人可能会把循环条件写成cur!=pq->tail,但这样是错误的,会少计算一个元素

队列的销毁

void QueueDestory(Queue* pq)//销毁
{assert(pq);QNode* cur = pq->head;while (cur) {QNode* next = cur->next;free(cur);cur = next;}pq->head = pq->tail = NULL;
}

销毁需要注意先要保存cur的下一个,不然会找不到,然后就是最后要把head和tail置为空

我们最后再来测试一下代码

没有问题,到这里,我们的栈和队列以及他们的接口就全部实现了,希望大家可以有所收获,下面我会附上全部代码

#pragma once
//Stack.h
#include <stdio.h>
#include<stdbool.h>
#include<assert.h>
#include<stdlib.h>
typedef int STDataType;typedef struct Stack {STDataType* a;int top;int capacity;
}ST;void StackInit(ST* ps);//初始化
void StackDestory(ST* ps);//销毁
void StackPush(ST* ps,STDataType x);//入栈
void StackPop(ST* ps);//出栈
STDataType StackTop(ST* ps);//返回栈顶元素
void StackSize(ST* ps);//计算数据个数
bool StackEmpty(ST* ps);//判断是否为空

 

//Stack.c
#include "Stack.h"
void StackInit(ST* ps)//初始化
{assert(ps);ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);if (ps->a == NULL) {printf("malloc fail\n");exit(-1);}ps->capacity = 4;ps->top = 0;
}
void StackDestory(ST* ps)//销毁
{assert(ps);free(ps->a);ps->a = NULL;ps->top = ps->capacity = 0;
}
void StackPush(ST* ps, STDataType x)//入栈
{assert(ps);if (ps->top == ps->capacity) {//满了扩容STDataType* tmp= (STDataType*)realloc(ps->a,ps->capacity*2*sizeof(STDataType));if (tmp == NULL) {printf("realloc fail\n");exit(-1);}else {ps->a = tmp;ps->capacity *= 2;}}ps->a[ps->top] = x;ps->top++;
}
void StackPop(ST* ps)//出栈
{assert(ps);assert(ps->top>0);ps->top--;
}
STDataType StackTop(ST* ps)//返回栈顶元素
{assert(ps);assert(ps->top > 0);return ps->a[ps->top - 1];
}
void StackSize(ST* ps)//计算数据个数
{assert(ps);return ps->top;
}
bool StackEmpty(ST* ps)//判断是否为空
{assert(ps);return ps->top == 0;
}
#pragma once
//Queue.h
#include <stdio.h>
#include<stdbool.h>
#include<assert.h>
#include<stdlib.h>
typedef int QDataType;typedef struct QueueNode
{struct QueueNode* next;QDataType data;
}QNode;typedef struct Queue
{QNode* head;QNode* tail;
}Queue;void QueueInit(Queue* pq);//初始化
void QueueDestory(Queue* pq);//销毁
void QueuePush(Queue* pq, QDataType x);//入队列
void QueuePop(Queue* pq);//出队列
QDataType QueueFront(Queue* pq);//取队头元素
QDataType QueueBack(Queue* pq);//取队尾元素
int QueueSize(Queue* pq);//取数据个数
bool QueueEmpty(Queue* pq);//判断是否为空
//Queue.c
#include"Queue.h"void QueueInit(Queue* pq)//初始化
{assert(pq);pq->head = pq->tail = NULL;
}
void QueueDestory(Queue* pq)//销毁
{assert(pq);QNode* cur = pq->head;while (cur) {QNode* next = cur->next;free(cur);cur = next;}pq->head = pq->tail = NULL;
}
void QueuePush(Queue* pq, QDataType x)//入队列
{assert(pq);QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL) {printf("malloc fail\n");exit(-1);}newnode->data = x;newnode->next = NULL;if (pq->tail == NULL) {pq->head = pq->tail = newnode;}else {pq->tail->next = newnode;pq->tail = newnode;}
}
void QueuePop(Queue* pq)//出队列
{assert(pq);assert(pq->head);if (pq->head->next == NULL) {free(pq->head);pq->head = pq->tail = NULL;}else {Queue* next = pq->head->next;free(pq->head);pq->head = next;}
}
QDataType QueueFront(Queue* pq)//取队头元素
{assert(pq);assert(pq->head);return pq->head->data;
}
QDataType QueueBack(Queue* pq)//取队尾元素
{assert(pq);assert(pq->head);return pq->tail->data;
}
int QueueSize(Queue* pq)//计算数据个数
{assert(pq);int size = 0;QNode* cur = pq->head;while (cur) {cur = cur->next;size++;}return size;
}
bool QueueEmpty(Queue* pq)//判断是否为空
{assert(pq);return pq->head == NULL;
}
//test.c
#include "Stack.h"
#include"Queue.h"
void testStack() {ST st;StackInit(&st);StackPush(&st, 1);StackPush(&st, 2);StackPush(&st, 3);StackPush(&st, 4);while (!StackEmpty(&st)) {printf("%d ", StackTop(&st));StackPop(&st);}StackDestory(&st);
}void testQueue() {Queue q;QueueInit(&q);QueuePush(&q, 1);QueuePush(&q, 2);QueuePush(&q, 3);QueuePush(&q, 4);while (!QueueEmpty(&q)) {printf("%d ", QueueFront(&q));QueuePop(&q);}QueueDestory(&q);
}
int main() {//testStack();testQueue();return 0;
}

如有错误,还请指正。

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

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

相关文章

适用更多会议场景,华为云会议的分组讨论功能来了!

适用更多会议场景&#xff0c;华为云会议的分组讨论功能来了&#xff01; 如今&#xff0c;线上沟通成为常态&#xff0c;线上会议更是成为工作推进过程中不可缺少的环节。但在一些场景中&#xff0c;例如在跨部门协调&#xff0c;沙龙研讨&#xff0c;教育培训或者招聘面试时&…

使用docker-compose部署达梦DEM管理工具,mac m1系列适用

之前搭建了mac m1下基于docker的达梦库&#xff08;地址&#xff09;&#xff0c;但是没有一个好用的管理端。 用过DBeaver&#xff0c;可以使用自定jar创建dm链接&#xff0c;只做简单查询还行&#xff0c;要是用到一些修改、大文本查看、配置修改等高级点的功能就不行了。 …

小啊呜产品读书笔记001:《邱岳的产品手记-12》第22讲 产品经理的图文基本功(上):产品文档 23讲产品经理的图文基本功(下):产品图例

小啊呜产品读书笔记001&#xff1a;《邱岳的产品手记-12》第22讲 产品经理的图文基本功&#xff08;上&#xff09;&#xff1a;产品文档 & 23讲产品经理的图文基本功&#xff08;下&#xff09;&#xff1a;产品图例一、今日阅读计划二、泛读&知识摘录1、第22讲 产品经…

m在ISE平台下使用verilog开发基于FPGA的GMSK调制器

目录 1.算法描述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法描述 高斯最小频移键控&#xff08;Gaussian Filtered Minimum Shift Keying&#xff09;&#xff0c;这是GSM系统采用的调制方式。数字调制解调技术是数字蜂窝移动通信系统空中接口的重要组成…

ES6 入门教程 26 编程风格 26.1 块级作用域 26.2 字符串 26.3 解构赋值

ES6 入门教程 ECMAScript 6 入门 作者&#xff1a;阮一峰 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删 文章目录ES6 入门教程26 编程风格26.1 块级作用域26.1.1 **let 取代 var**26.1.2 **全局常量和线程安全**26.2 字符串26.3 解构赋值26 编程风格 …

【算法基础】P问题、NP问题、NP-Hard问题、NP-Complete问题

P问题、NP问题、NP-Hard问题、NP-Complete问题前提1. 时间复杂度&#xff1a;2. 约化(Reducibility)P问题NP问题NPHard问题NP-Complete问题其它&#xff1a;前提 1. 时间复杂度&#xff1a; 2. 约化(Reducibility) 如果能找到一个变化法则&#xff0c;对任意一个A程序的输入&…

TOWER 成就徽章 NFT 系列介绍——TOWER 生态系统的第一个灵魂通证(SBT)

2022 年 7 月&#xff0c;团队推出了成就徽章 NFT 系列&#xff0c;记录每个成员在 TOWER 生态系统中的努力。这是第一个不可转让的灵魂 NFT 系列&#xff08;SBT&#xff09;&#xff0c;代表了每个玩家的独特身份。 关于灵魂通证&#xff08;SBT&#xff09; 以太坊联合创始人…

linux的重定向与xshell原理

文章目录一、重定向1.输出重定向&#xff1a;>1.写入指定文件2. 覆盖写2.追加重定向 &#xff1a;>>3.输出重定向&#xff1a;<1.键盘显示2.文件显示4.重定向的一些认知误区1. test.c只显示错误的2. msg.c只显示正确的3.分析4.显示出正确的二 、xshell命令及原理1.…

2023年第三届智能制造与自动化前沿国际会议(CFIMA 2023)

2023年第三届智能制造与自动化前沿国际会议(CFIMA 2023) 重要信息 会议网址&#xff1a;www.cfima.org 会议时间&#xff1a;2023年6月9-11日 召开地点&#xff1a;中国大理 截稿时间&#xff1a;2023年4月20日 录用通知&#xff1a;投稿后2周内 收录检索&#xff1a;EI,…

关于SD-WAN的十问十答(最强攻略戳这里)

1. WAN和SD-WAN之间的区别&#xff1f; 从底层来看&#xff0c;相较基于硬件物理设施的WAN&#xff0c;SD-WAN是一种覆盖现有网络的软件技术&#xff0c;是部署在物理基础设施下层的流量管理网络。 和常规WAN相比&#xff0c;SD-WAN具有虚拟WAN体系结构和软件驱动技术&#xff…

国内优秀的多用户商城系统盘点(2022年整理)

电商战略时代&#xff0c;越来越多的企业或商家选择将消费者引入自己建设的独立商城&#xff0c;如零食行业的良品铺子、三只松鼠&#xff0c;从而打造属于自己的IP形象。此时&#xff0c;挑选一款优秀的商城源码是企业的不二之选&#xff0c;既降低了电商从业者和创业者的入门…

hive表加载csv格式数据或者json格式数据

先说简单的使用 CREATE TABLE cc_test_serde( id string COMMENT from deserializer, name string COMMENT from deserializer) ROW FORMAT SERDE org.apache.hadoop.hive.serde2.JsonSerDe STORED AS INPUTFORMAT org.apache.hadoop.mapred.TextInputFormat OUTPUTFO…

决策树-相关作业

1. 请使用泰勒展开推导gini不纯度公式&#xff1b; 2. 请说明树的剪枝怎么实现&#xff1b; ●预剪枝&#xff08;pre-pruning&#xff09;通过替换决策树生成算法中的停止准则。&#xff08;例如&#xff0c;最大树深度或信息增益大于某一阈值&#xff09;来实现树的简化。预…

Mybatis-plus通过exists判断记录是否存在

Mybatis-plus通过exists判断记录是否存在一、Controller二、Service三、效果一、Controller GetMapping("/queryNewProductExists")public Boolean queryNewProductExists(RequestParam("name") String name) {return opProductService.queryNewProductExi…

基于sklearn的集成学习实战

集成学习投票法与bagging 投票法 sklearn提供了VotingRegressor和VotingClassifier两个投票方法。使用模型需要提供一个模型的列表&#xff0c;列表中每个模型采用tuple的结构表示&#xff0c;第一个元素代表名称&#xff0c;第二个元素代表模型&#xff0c;需要保证每个模型…

CDGA|从平台自治到规范化的数据治理

数字时代&#xff0c;大型平台构建起局部市场&#xff0c;有众多市场主体在其上从事经济活动和社会交往。 平台上大量生产者消费者聚集产生的交易海量且高频&#xff0c;要处理的纠纷和各种问题数量巨大&#xff0c;远超出传统政府监管能力&#xff0c;事态变化之快速也远超出法…

Python实现BP神经网络ANN单隐层回归模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 20世纪80年代中期&#xff0c;David Runelhart。Geoffrey Hinton和Ronald W-llians、DavidParker等人分…

Web3D应用开发在线IDE【中文版】

nunuStudio 是一个Web 3D应用程序的集成开发环境&#xff0c;它提供用于在 3D 世界中创建和编辑对象的工具&#xff0c;支持JavaScript和Python对3D场景进行二次开发。nunuStudio中文版 由 BimAnt 提供。 如果你曾经使用过其他类似的框架&#xff08;unity、playcanvas、godot …

国网云(华为组件)使用

一、国网云(华为组件)介绍 一、项目各项环境 各项环境的介绍 MRS-Hive:MRS支持在大数据存储量大,计算资源需要弹性扩展的场景下,用户将数据存储在OBS服务中。使用MRS集群仅做数据计算处理的存算分离模式。DWS(高斯200):云原生数据库Gauss DB(DWS)1:融合分析能力是云原…

构建镜像开源工具 buildah

构建镜像开源工具 buildah tags: images 文章目录构建镜像开源工具 buildah1. 简介2. 特点3. Buildah 和 Podman4. 安装4.1 CentOS4.2 Ubuntu4.3 RHEL74.4 Fedora5. 命令6. 示例6.1 命令行构建一个 httpd 镜像6.2 Dockerfile 构建6.3 构建镜像脚本&#xff08;代替 Dockerfil…