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

news2024/9/29 5:32:34

 第三部分、栈(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);程序停止(#):
#

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

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

相关文章

vscode+opencv基础用法学习1

案例1&#xff1a;读取图片信息 如果是使用云服务器的话&#xff0c;由于图形界面的问题&#xff0c;使用cv::show来显示图片会报错 // 图片的读取和显示 // 导入opencv头文件 #include "opencv2/opencv.hpp" #include <iostream>int main(int argc, char** …

数据仓库 Apache Hive

一、数据分析 1、数据仓库 数据仓库&#xff08;英语&#xff1a;Data Warehouse&#xff0c;简称数仓、DW&#xff09;&#xff0c;是一个用于存储、分析、报告的数据系统。 数据仓库的目的是构建面向分析的集成化数据环境&#xff0c;分析结果为企业提供决策支持&#xff08…

js:使用canvas画一个半圆

背景 需求需要画一个半圆&#xff0c;或者多半圆&#xff0c;其实一下子就能想到 canvas 中的圆弧&#xff0c;核心使用 context.arc context.arc(x,y,r,sAngle,eAngle,counterclockwise)接下来我们看看示例 例一 <!DOCTYPE html> <html lang"en"> &…

机器学习基本算法:算法流程和算法分类

1、算法流程 机器学习的过程是一个完整的项目周期&#xff0c;其中包括数据的采集、数据的特征提取与分类&#xff0c;之后采用何种算法去创建机器学习模型从而获得预测数据。 算法流程 从上图可以看出一个完整的机器学习项目包含以下这些内容&#xff1a; 输入数据&#x…

JAVA毕业设计121—基于Java+Springboot的房屋租赁管理系统(源代码+数据库+9000字文档)

毕设所有选题&#xff1a; https://blog.csdn.net/2303_76227485/article/details/131104075 基于JavaSpringboot的房屋租赁管理系统(源代码数据库9000字文档)121 一、系统介绍 本项目还有ssm版本&#xff0c;分为用户、房东、管理员三种角色 1、用户&#xff1a; 注册、登…

Ubuntu 卸载重装 Nvidia 显卡驱动

问题描述 我使用 airsim 的时候&#xff0c;发现 UE4 没法使用显卡&#xff0c;导致非常卡顿 输入 nvidia-smi 有显卡型号等信息的输出&#xff0c;但是进程 process 里面没有显示 airsim 和其他软件占用显卡情况 因此&#xff0c;我选择了卸载重装 一.卸载旧版本的驱动 …

Komodor:Kubernetes 监控工具全面指南

为了方便起见&#xff0c;Komodor 提供了一个简单的 Web 界面&#xff0c;以帮助您监控 Kubernetes 集群的状态。它拥有付费和免费增值计划&#xff0c;除了在出现问题时通知用户外&#xff0c;还拥有一系列方便的工具&#xff0c;用于跟踪和管理集群中部署的资源的状态。让我们…

Python集合(set)

目录 集合创建集合访问集合向集合中添加和删除元素集合的 交集&#xff0c;并集&#xff0c;差集运算**交集****并集****差集** 集合方法 集合 集合是无序和无索引的集合。在 Python 中&#xff0c;集合用花括号编写。 创建集合 创建集合&#xff1a; thisset {"a"…

RISC-V Bytes: Caller and Callee Saved Registers

原文链接1&#xff1a;https://danielmangum.com/posts/risc-v-bytes-caller-callee-registers/ 原文链接2&#xff1a;https://zhuanlan.zhihu.com/p/77663680 //主要讲栈帧 原文链接3&#xff1a;https://www.jianshu.com/p/b666213cdd8a //主要讲栈帧 This is part of a new…

Ftrans飞驰云联荣获“CSA 2023安全创新奖”

2023年12月21日&#xff0c;第七届云安全联盟大中华区大会在深圳成功举办。会上&#xff0c;CSA大中华区发布了多个研究成果并进行 CSA 2023年度颁奖仪式&#xff0c;Ftrans飞驰云联以其突出的技术创新能力和广泛的市场应用前景&#xff0c;荣获备受瞩目的“CSA 2023安全创新奖…

redis夯实之路-哨兵(Sentinel)机制详解

Sentinel&#xff08;哨兵&#xff09;保证了redis的高可用性&#xff0c;一个Sentinel或多个Sentinel组成的系统监视多个主从服务器&#xff0c;当主服务器下线时&#xff0c;自动将一个从服务器升级为主服务器。 sentinel的主要功能 集群监控&#xff1a;负责监控redis mas…

imgaug库指南(20):从入门到精通的【图像增强】之旅

引言 在深度学习和计算机视觉的世界里&#xff0c;数据是模型训练的基石&#xff0c;其质量与数量直接影响着模型的性能。然而&#xff0c;获取大量高质量的标注数据往往需要耗费大量的时间和资源。正因如此&#xff0c;数据增强技术应运而生&#xff0c;成为了解决这一问题的…

【Python】使用Opencv裁剪指定区域,再重构大小和保存示例

在Python中&#xff0c;使用OpenCV库可以很方便地截取图像的某一区域&#xff0c;然后尺寸重构&#xff0c;最后保存为新的图像文件。以下是一个示例代码&#xff0c;演示如何实现这一操作&#xff1a; import cv2# 读取图像 img cv2.imread(img.jpg)# 定义截取的区域&#x…

Istio 实战:WasmPlugin(Proxy-Wasm 插件)开发(实现限流和修改请求和响应的 header)

更多 istio 文章&#xff1a;Istio 专栏目录 WasmPlugin 的典型应用 限流&#xff1a;当前 envoy 提供的限流能力虽然比较强大&#xff0c;但主要提供了一些 api&#xff0c;在使用上对用户不够友好&#xff0c;且全局限流对每个请求都调用一次限流服务&#xff0c;性能损耗较…

SSM-SpringMVC+Spring+Mybatis

创建项目 创建好 项目后, 项目目录分析 数据库设计 我们采用员工表 Employee 与 部门表 Department 部门表 表设计--- 员工表 --表设计 因为有文件上传操作,因此 建立 info表 (其中 员工只能隶属一个部门,因此 两张表之间 有外键关系) java 代码 设计 数据库建立完毕后,需要…

自定义数据实现SA3D

SA3D&#xff1a;Segment Anything in 3D with NeRFs 实现了3D目标分割 原理是利用SAM(segment anything) 模型和Nerf分割渲染3D目标&#xff0c; SAM只能分块&#xff0c;是没有语义标签的&#xff0c;如何做到语义连续&#xff1f; SA3D中用了self-prompt, 根据前一帧的mask…

C#编程-了解线程的优先级

了解线程的优先级 控制线程行为的一个属性是它的优先级。.NET运行时环境基于它们的优先级执行线程。CPU一次仅执行一个线程。因此,处于执行的可运行状态的线程,排队等待轮到被处理器执行。线程是固定优先级调度的。带有优先级的每个线程在处理器的线程队列中有自己的位置。 …

Java面试之虚拟机

1、前言 本篇的面试题基于网络整理&#xff0c;和自己编辑。在不断的完善补充哦。 2、什么是虚拟机&#xff1f; Java 虚拟机&#xff0c;是一个可以执行 Java 字节码的虚拟机进程。Java 源文件被编译成能被 Java 虚拟机执行的字节码文件( .class )。 Java 被设计成允许应用程…

老胡的周刊(第124期)

老胡的信息周刊[1]&#xff0c;记录这周我看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。 &#x1f3af; 项目 QAnything[2] 开源的企业级本地知识库问答解…

Java入门IDEA基础语法

1&#xff1a;Java入门 1.1 Java简介 Java是什么&#xff1a; Java是一门非常优秀的计算机语言 语言&#xff1a;人与人交流沟通的表达方式 计算机语言&#xff1a;人与计算机之间进行信息交流沟通的一种特殊语言 Java之父&#xff1a;詹姆斯高斯林&#xff08;James Gosli…