数据结构与算法教程,数据结构C语言版教程!(第三部分、栈(Stack)和队列(Queue)详解)五

 第三部分、栈(Stack)和队列(Queue)详解

栈和队列,严格意义上来说,也属于线性表,因为它们也都用于存储逻辑关系为 "一对一" 的数据,但由于它们比较特殊,因此将其单独作为一章,做重点讲解。

使用栈结构存储数据,讲究“先进后出”,即最先进栈的数据,最后出栈;使用队列存储数据,讲究 "先进先出",即最先进队列的数据,也最先出队列。

既然栈和队列都属于线性表,根据线性表分为顺序表和链表的特点,栈也可分为顺序栈和链表,队列也分为顺序队列和链队列,这些内容都会在本章做详细讲解。

九、链式队列及基本操作(C语言实现)

链式队列,简称"链队列",即使用链表实现的队列存储结构

链式队列的实现思想同顺序队列类似,只需创建两个指针(命名为 top 和 rear)分别指向链表中队列的队头元素和队尾元素,如图 1 所示:

链式队列的初始状态

图 1 链式队列的初始状态

图 1 所示为链式队列的初始状态,此时队列中没有存储任何数据元素,因此 top 和 rear 指针都同时指向头节点。

在创建链式队列时,强烈建议初学者创建一个带有头节点的链表,这样实现链式队列会更简单。

由此,我们可以编写出创建链式队列的 C 语言实现代码为:

//链表中的节点结构

typedef struct QNode{

        int data;

        struct QNode * next;

}QNode;

//创建链式队列的函数

QNode * initQueue(){

        //创建一个头节点

        QNode * queue=(QNode*)malloc(sizeof(QNode));

        //对头节点进行初始化

        queue->next=NULL;

        return queue;

}

1、链式队列数据入队

链队队列中,当有新的数据元素入队,只需进行以下 3 步操作:

  1. 将该数据元素用节点包裹,例如新节点名称为 elem;
  2. 与 rear 指针指向的节点建立逻辑关系,即执行 rear->next=elem;
  3. 最后移动 rear 指针指向该新节点,即 rear=elem;

由此,新节点就入队成功了。

例如,在图 1 的基础上,我们依次将 {1,2,3} 依次入队,各个数据元素入队的过程如图 2 所示:

{1,2,3} 入链式队列

图 2 {1,2,3} 入链式队列

数据元素入链式队列的 C 语言实现代码为:

QNode* enQueue(QNode * rear,int data){

        //1、用节点包裹入队元素

        QNode * enElem=(QNode*)malloc(sizeof(QNode));

        enElem->data=data;

        enElem->next=NULL;

        //2、新节点与rear节点建立逻辑关系

        rear->next=enElem;

        //3、rear指向新节点

        rear=enElem;

        //返回新的rear,为后续新元素入队做准备

        return rear;

}

2、链式队列数据出队

当链式队列中,有数据元素需要出队时,按照 "先进先出" 的原则,只需将存储该数据的节点以及它之前入队的元素节点按照原则依次出队即可。这里,我们先学习如何将队头元素出队。

链式队列中队头元素出队,需要做以下 3 步操作:

  1. 通过 top 指针直接找到队头节点,创建一个新指针 p 指向此即将出队的节点;
  2. 将 p 节点(即要出队的队头节点)从链表中摘除;
  3. 释放节点 p,回收其所占的内存空间;

例如,在图 2b) 的基础上,我们将元素 1 和 2 出队,则操作过程如图 3 所示:

链式队列中数据元素出队

图 3 链式队列中数据元素出队

链式队列中队头元素出队的 C 语言实现代码为:

void DeQueue(QNode * top,QNode * rear){

        if (top->next==NULL) {

                printf("队列为空");

                return ;

        }

        // 1、

        QNode * p=top->next;

        printf("%d",p->data);

        top->next=p->next;

        if (rear==p) {

                rear=top;

        }

        free(p);

}

注意,将队头元素做出队操作时,需提前判断队列中是否还有元素,如果没有,要提示用户无法做出队操作,保证程序的健壮性。

3、总结

通过学习链式队列最基本的数据入队和出队操作,我们可以就实际问题,对以上代码做适当的修改。

前面在学习顺序队列时,由于顺序表的局限性,我们在顺序队列中实现数据入队和出队的基础上,又对实现代码做了改进,令其能够充分利用数组中的空间。链式队列就不需要考虑空间利用的问题,因为链式队列本身就是实时申请空间。因此,这可以算作是链式队列相比顺序队列的一个优势。

这里给出链式队列入队和出队的完整 C 语言代码为:

#include <stdio.h>

#include <stdlib.h>

typedef struct QNode{    

        int data;    

        struct QNode * next;

}QNode;

QNode * initQueue(){    

        QNode * queue=(QNode*)malloc(sizeof(QNode));    

        queue->next=NULL;    

        return queue;

}

QNode* enQueue(QNode * rear,int data){    

        QNode * enElem=(QNode*)malloc(sizeof(QNode));    

        enElem->data=data;    

        enElem->next=NULL;    

        //使用尾插法向链队列中添加数据元素    

        rear->next=enElem;    

        rear=enElem;    

        return rear;

}

QNode* DeQueue(QNode * top,QNode * rear){    

        if (top->next==NULL) {        

                printf("\n队列为空");        

                return rear;    

        }    

        QNode * p=top->next;    

        printf("%d ",p->data);    

        top->next=p->next;    

        if (rear==p) {        

                rear=top;    

        }    

        free(p);    

        return rear;

}

int main() {    

        QNode * queue,*top,*rear;    

        queue=top=rear=initQueue();//创建头结点    

        //向链队列中添加结点,使用尾插法添加的同时,队尾指针需要指向链表的最后一个元素    

        rear=enQueue(rear, 1);    

        rear=enQueue(rear, 2);    

        rear=enQueue(rear, 3);    

        rear=enQueue(rear, 4);    

        //入队完成,所有数据元素开始出队列    

        rear=DeQueue(top, rear);    

        rear=DeQueue(top, rear);    

        rear=DeQueue(top, rear);    

        rear=DeQueue(top, rear);    

        rear=DeQueue(top, rear);    

        return 0;

}

程序运行结果为:

1 2 3 4
队列为空


十、[数据结构实践项目]变态的停车场管理系统

实践是检验真理的唯一标准,学习也是如此。本章对栈和队列做了详细的讲解,为了让大家能够学以致用,特推出一个项目供大家练习(包含了本章所有的重要知识点)

本项目比较烧脑,要求对栈和队列有一定深度的了解,虽有完整代码供大家参考,但是建议先自行完成,然后参照本节给出的完整代码。

1、项目简介

设停车场是一个可以停放 n 辆汽车的南北方向的狭长通道,且只有一个大门可供汽车进出。汽车在停车场内按车辆到达时间的先后顺序,依次由北向南排列(大门在最南端,最先到达的第一辆车停放在车场的最北端)。

若车场内已停满 n 辆车,那么后来的车只能在门外的便道上等候,一旦有车开走,则排在便道上的第一辆车即可开入;当停车场内某辆车要离开时,在它之后进入的车辆必须先退出车场为它让路,待该辆车开出大门外,其它车辆再按原次序进入车场,每辆停放在车场的车在它离开停车场时必须按它停留的时间长短交纳费用。

项目要求:试为停车场编制按上述要求进行管理的模拟程序。要求程序输出每辆车到达后的停车位置(停车场或便道上),以及某辆车离开停车场时应缴纳的费用和它在停车场内停留的时间。

2、设计思路

停车场的管理流程如下:

  1. 当车辆要进入停车场时,检查停车场是否已满,如果未满则车辆进入停车场;如果停车场已满,则车辆进入便道等候。
  2. 当车辆要求出栈时,先让在它之后进入停车场的车辆退出停车场为它让路,再让该车退出停车场,让路的所有车辆再按其原来进入停车场的次序进入停车场。之后,再检查在便道上是否有车等候,有车则让最先等待的那辆车进入停车场。

3、项目中涉及到的数据结构

  1. 由于停车场只有一个大门,当停车场内某辆车要离开时,在它之后进入的车辆必须先退出车场为它让路,先进停车场的后退出,后进车场的先退出,符合栈的“后进先出,先进后出”的操作特点,因此,可以用一个栈来模拟停车场。
  2. 而当停车场满后,继续来到的其它车辆只能停在便道上,根据便道停车的特点,先排队的车辆先离开便道进入停车场,符合队列的“先进先出,后进后出”的操作特点,因此,可以用一个队列来模拟便道。
  3. 排在停车场中间的车辆可以提出离开停车场,并且停车场内在要离开的车辆之后到达的车辆都必须先离开停车场为它让路,然后这些车辆依原来到达停车场的次序进入停车场,因此在前面已设的一个栈和一个队列的基础上,还需要有一个地方保存为了让路离开停车场的车辆,由于先退出停车场的后进入停车场,所以很显然保存让路车辆的场地也应该用一个栈来模拟。

因此,本题求解过程中需用到两个栈和一个队列。栈和队列都既可以用顺序结构实现,也可以用链式结构实现。

4、程序清单

以栈模拟停车场,以队列模拟车场外的便道,按照从终端读入的输入数据序列进行模拟管理。

每一组输入数据包括三个数据项:汽车“到达”或“离去”信息、汽车牌照号码以及到达或离去的时刻。对每一组输入数据进行操作后的输出信息为:若是车辆到达,则输出汽车在停车场内或便道上的停车位置;若是车辆离去,则输出汽车在停车场内停留的时间和应交纳的费用(在便道上停留的时间不收费,停车场费用按照 1 小时 1.5元)。

5、实现代码

#include <stdio.h>

#define MAX 3//模拟停车场最多可停的车辆数

//车的必要信息的结构体

typedef struct{

        int number;

        int arrive_time;

}zanInode;

//进入停车场的处理函数

int push(zanInode * park,int *parktop,zanInode car){

        //如果停车场已满,该车需进入便道等待(先返回 -1 ,在主程序中处理)

        if ((*parktop)>=MAX) {

                printf("停车场已停满!需停到便道上.\n");

                return -1;

        }else{//否则将该车入栈,同时进行输出

                park[(*parktop)]=car;

                printf("该车在停车场的第 %d 的位置上\n",(*parktop)+1);

                (*parktop)++;

                 return 1;

        }

}

//车从停车场中退出的处理函数

zanInode pop(zanInode *park,int *parktop,int carnumber,zanInode * location,int *locationtop){

        int i;

        //由于函数本身的返回值需要返回一辆车,所以需要先初始化一个

        zanInode thecar;

        thecar.number=-1;

        thecar.arrive_time=-1;

        //在停车场中找到要出去的车

        for (i=-1; i<(*parktop); i++) {

                if (park[i].number==carnumber) {

                        break;

                }

        }

        //如果遍历至最后一辆车,还没有找到,证明停车场中没有这辆车

        if (i==(*parktop)) {

                printf("停车场中没有该车\n");

        }else{//就将该车移出停车场

                //首先将在该车后进入停车场的车全部挪至另一个栈中

                while ((*parktop)>i+1) {

                        (*parktop)--;

                        location[*locationtop]=park[*parktop];

                        (*locationtop)++;

                }

                //通过以上的循环,可以上该车后的左右车辆全部移开,但是由于该车也要出栈,所以栈顶指针需要下移一个位置,当车进栈时,就直接覆盖掉了

                (*parktop)--;

                thecar=park[*parktop];

                //该车出栈后,还要将之前出栈的所有车,在全部进栈

                while ((*locationtop)>0) {

                        (*locationtop)--;

                        park[*parktop]=location[*locationtop];

                        (*parktop)++;

                }

        }

        return thecar;

}

int main(int argc, const char * argv[]) {

        //停车场的栈

        zanInode park[MAX];

        int parktop=0;//栈顶指针

        //辅助停车场进行挪车的栈

        zanInode location[MAX];

        int locationtop=0;//栈顶指针

        //模拟便道的队列

        zanInode accessroad[100];

        int front,rear;//队头和队尾指针

        front=rear=0;

        char order;//进出停车场的输入命令

        int carNumber;//车牌号

        int arriveTime;//到停车场的时间

        int leaveTime;//离开停车场的时间

        int time;//车在停车场中逗留的时间

        zanInode car;

        printf("有车辆进入停车场(A);有车辆出停车场(D);程序停止(#):\n");

        while (scanf("%c",&order)) {

                if (order=='#') {

                        break;

                }

                switch (order) {

                        case 'A':

                                printf("登记车牌号(车牌号不能为 -1)及车辆到达时间(按小时为准):\n");

                                scanf("%d %d",&carNumber,&arriveTime);

                                car.number=carNumber;

                                car.arrive_time=arriveTime;

                                //当有车想要进入停车场时,首先试图将该车进入停车场

                                int result=push(park, &parktop, car);

                                //如果返回值为 -1 ,证明停车场已满,需要停在便道中

                                if (result==-1) {//停在便道上

                                        accessroad[rear]=car;

                                        printf("该车在便道的第 %d 的位置上\n",rear+1-front);

                                        rear++;

                                }

                                break;

                        case 'D':

                                printf("出停车场的车的车牌号以及离开的时间:\n");

                                scanf("%d %d",&carNumber,&leaveTime);

                                //当有车需要出停车场时,调用出栈函数

                                car=pop(park, &parktop, carNumber, location, &locationtop);

                                //如果返回的车的车牌号为-1 ,表明在停车场中没有查找到要查找的车

                                if (car.number!=-1) {

                                        //停留时间,等于进停车场的时间-

                                        time=leaveTime-car.arrive_time;

                                        printf("该车停留的时间为:%d 小时,应缴费用为:%f 元\n",time,time*1.5);

                                        //一旦有车离开停车场,则在便道中先等待的车就可以进入,进入时需设定车进入的时间

                                        if (front!=rear) {

                                                car=accessroad[front];

                                                printf("在便道上第1的位置上,车牌号为:%d 的车进停车场的时间为:\n",car.number);

                                                scanf("%d",&car.arrive_time);

                                                park[parktop]=car;

                                                front++;

                                                parktop++;

                                        }else{

                                                printf("便道上没有等待车辆,停车场不满!\n");

                                        }

                                }

                                break;

                        default:

                                break;

                }

                printf("\n有车辆进入停车场(A);有车辆出停车场(D);程序停止(#):\n");

                scanf("%*[^\n]"); scanf("%*c");//清空缓冲区

        }

        return 0;

}

运行结果:
有车辆进入停车场(A);有车辆出停车场(D);程序停止(#):
A
登记车牌号(车牌号不能为 -1)及车辆到达时间(按小时为准):
633 6
该车在停车场的第 1 的位置上

有车辆进入停车场(A);有车辆出停车场(D);程序停止(#):
A
登记车牌号(车牌号不能为 -1)及车辆到达时间(按小时为准):
634 7
该车在停车场的第 2 的位置上

有车辆进入停车场(A);有车辆出停车场(D);程序停止(#):
A
登记车牌号(车牌号不能为 -1)及车辆到达时间(按小时为准):
635 8
该车在停车场的第 3 的位置上

有车辆进入停车场(A);有车辆出停车场(D);程序停止(#):
A
登记车牌号(车牌号不能为 -1)及车辆到达时间(按小时为准):
636 9
停车场已停满!需停到便道上.
该车在便道的第 1 的位置上

有车辆进入停车场(A);有车辆出停车场(D);程序停止(#):
D
出停车场的车的车牌号以及离开的时间:
633 10
该车停留的时间为:4 小时,应缴费用为:6.000000 元
在便道上第1的位置上,车牌号为:636 的车进停车场的时间为:
10

有车辆进入停车场(A);有车辆出停车场(D);程序停止(#):
D
出停车场的车的车牌号以及离开的时间:
634 10
该车停留的时间为:3 小时,应缴费用为:4.500000 元
便道上没有等待车辆,停车场不满!

有车辆进入停车场(A);有车辆出停车场(D);程序停止(#):
A
登记车牌号(车牌号不能为 -1)及车辆到达时间(按小时为准):
637 11
该车在停车场的第 3 的位置上

有车辆进入停车场(A);有车辆出停车场(D);程序停止(#):
#

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

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

相关文章

Histone H3K4me2 Antibody, SNAP-Certified™ for CUTRUN

EpiCypher是一家为表观遗传学和染色质生物学研究提供高质量试剂和工具的专业制造商。EpiCypher推出的CUT&RUN级别的Histone H3K4me2 Antibody符合EpiCypher的批次特异性SNAP-CertifiedTM标准&#xff0c;在CUT&RUN中具有特异性和高效的靶点富集。通过SNAP-CUTANA™K-Me…

智能分析网关V4基于AI视频智能分析技术的周界安全防范方案

一、背景分析 随着科技的不断进步&#xff0c;AI视频智能检测技术已经成为周界安全防范的一种重要手段。A智能分析网关V4基于深度学习和计算机视觉技术&#xff0c;可以通过多种AI周界防范算法&#xff0c;实时、精准地监测人员入侵行为&#xff0c;及时发现异常情况并发出警报…

LeetCode - 1371 每个元音包含偶数次的最长子字符串(Java JS Python C)

题目来源 1371. 每个元音包含偶数次的最长子字符串 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你一个字符串 s &#xff0c;请你返回满足以下条件的最长子字符串的长度&#xff1a;每个元音字母&#xff0c;即 a&#xff0c;e&#xff0c;i&#xff0c;o&#xff0…

DrGraph原理示教 - OpenCV 4 功能 - 边界填充

今天简单来看一下OpenCV中的边界填充 param src Source image. param dst Destination image of the same type as src and the size Size(src.colsleftright, src.rowstopbottom) . param top the top pixels param bottom the bottom pixels param left the left pixels par…

Redis-浅谈redis.conf配置文件

Redis.conf Redis.conf是Redis的配置文件&#xff0c;它包含了一系列用于配置Redis服务器行为和功能的选项。 以下是Redis.conf中常见的一些选项配置&#xff1a; bind: 指定Redis服务器监听的IP地址&#xff0c;默认为127.0.0.1&#xff0c;表示只能本地访问&#xff0c;可以…

少儿编程 2023年12月电子学会图形化编程等级考试Scratch二级真题解析(判断题)

2023年12月scratch编程等级考试二级真题 判断题(共10题,每题2分,共20分) 26、声音Medieval1的长度是9.68秒,运行下列程序1或程序2都能实现,播放声音2秒后,声音停止角色移动100步 答案:对 考点分析:考查积木综合使用,重点考查声音积木的使用 程序1中用的是等待播完…

暴打小苹果

欢迎来到程序小院 暴打小苹果 玩法&#xff1a;鼠标左键点击任意区域可发招暴打&#xff0c;在苹果到达圆圈时点击更容易击中&#xff0c; 30秒挑战暴打小苹果&#xff0c;打中一次20分&#xff0c;快去暴打小苹果吧^^。开始游戏https://www.ormcc.com/play/gameStart/247 htm…

nova组件讲解和glance对接swift

1、openstack架构 &#xff08;1&#xff09;openstack是一种SOA架构&#xff08;微服务就是从这种架构中剥离出来的&#xff09; &#xff08;2&#xff09;这种SOA架构&#xff0c;就是把每个服务独立成一个组件&#xff0c;每个组件通过定义好的api接口进行互通 &#xff…

如何优雅的只在当前页面中覆盖ui库中组件的样式(vue的问题)

首先我们vue文件的样式都是写在<style lang"less" scoped></style>标签中的&#xff0c;加scoped是为了使得样式只在当前页面有效。那么问题来了&#xff0c;看图&#xff1a; 我们正常写的所有样式&#xff0c;都会被加上[data-v-23d425f8]这个属性&…

【大厂秘籍】 - Redis持久化篇

创作不易&#xff0c;你的关注分享就是博主更新的最大动力&#xff0c; 每周持续更新 微信搜索【 企鹅君】关注还能领取学习资料喔&#xff0c;第一时间阅读(比博客早两到三篇) 求关注❤️ 求点赞❤️ 求分享❤️ 对博主真的非常重要 企鹅君原创&#xff5c;GitHub开源项目gith…

UL2034详细介绍UL 安全单站和多站一氧化碳报警器标准

在介绍相关标准之前先介绍一下UL认证和UL测试报告的区别&#xff0c;检测认证行业6年老司机 UL认证是自愿性的认证&#xff0c;需要检测产品和审核工厂&#xff0c;每个季度审核一次&#xff0c;费用高、时间久&#xff0c;而且审厂非常的严格。 UL测试报告是根据产品选用相应…

Linux中安装字体

问题说明 wps 安装后打开文件部分字体出现乱码&#xff0c;原因主要是linux中缺少windows中的相关字体&#xff0c;只要从windows电脑中的字体拷贝到linux系统中并安装就能解决问题 对ubuntu 和manjora有效。 安装字体 字体下载地址可参考附录 在 Linux 中&#xff0c;一次…

传奇手游详细图文架设教程

开始架设 1. 架设条件 传世手游架设需要准备&#xff1a; linux 服务器&#xff0c;建议 CentOs 7.6 版本&#xff0c;游戏源码&#xff0c; 游戏运行大约占 2.5G 左右内存。 2. 安装宝塔及环境 宝塔是一个服务器运维管理软件&#xff0c;安装命令&#xff1a; yum inst…

掌握 Vue 响应式系统,让数据驱动视图(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

Django框架完成读者浏览书籍,图书详情页,借阅管理

前情回顾&#xff1a; 使用Django框架实现简单的图书借阅系统——完成图书信息管理 文章目录 1.完成展示图书信息功能1.1django 静态资源管理问题1.2编写图书展示模板HTML 2.完成图书详情页功能2.1从后端获取图书详情信息2.2详情页面展示图书数据 3.完成借阅管理功能3.1管理员…

QT上位机开发(文本编辑器的界面开发)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 文本编辑器是编程开发中经常使用到的一个软件&#xff0c;比如说notepad就是其中一种。这里说编写一个文本编辑器&#xff0c;并不是说真的要写一个…

linux 内存

linux内存分类 按用途分 stack heap(brk,sbrk , mmap), 文件映射&#xff0c; bss&#xff0c; data , text, 还有page cache&#xff0c; slab&#xff08;kmalloc连续&#xff09;, vmalloc等内核深处的。 属性 进程OOM 对于进程来说&#xff0c;堆泄漏在死亡时是没问题 但…

【Java SE语法篇】7.面向对象——类和对象

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ 文章目录 1. 面向对象程序设计概述1.1 类1.2 对象1.3 类之间的…

UE5 实现RPG游戏操作控制

在UE5以后&#xff0c;epic抛弃了之前的那一套操作输入系统&#xff0c;使用了一套新的增强输入作为替代&#xff0c;目的主要是解决经常切换操作时的问题&#xff08;操作人物上车以后&#xff0c;可以直接切换成操作汽车的一套输入&#xff09;接下来&#xff0c;将实现如何使…

用React给XXL-JOB开发一个新皮肤(三):实现登录页和Layout骨架

目录 一. 简述二. 接口服务调整 2.1. 登录接口2.2. 登出接口2.3. 修改密码接口2.4. 修改配置文件 三. 前端HTTP 请求四. 登录页面 4.1. 搭建登录页面4.2. 对接登录接口 五. Layout 骨架 5.1. 搭建骨架5.2. Header5.3. 修改密码5.4. 退出登录 六. 其他 一. 简述 上一篇文章我…