数据结构与算法教程,数据结构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);程序停止(#):
#

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

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

相关文章

Java Swing 图书借阅系统 窗体项目 期末课程设计 窗体设计

视频教程&#xff1a; 【课程设计】图书借阅系统 功能描述&#xff1a; 图书管理系统有三个角色&#xff0c;系统管理员、图书管理员、借阅者&#xff1b; 系统管理员可以添加借阅用户&#xff1b; ​图书管理员可以添加图书&#xff0c;操作图书借阅和归还&#xff1b; 借…

c++例题2点和直线关系

#include<iostream> #include<string> using namespace std; //圆的类 class yuan{ public:int x2 10;int y2 10;int r 5; }; //点的类 class dian{ public :void setx(int x){x1 x;}int getx(){return x1;}void sety(int y){y1 y;}int gety(){return y1;} pr…

Lagrange对偶法

这里写自定义目录标题 5.1.1 The Lagrangian5.1.2 The Lagrange dual function5.2 The Lagrange dual problem5.2.3 Strong duality and Slater’s constraint qualification5.2.3 Strong duality and Slater’s constraint qualification5.5.3 KKT optimality conditions Lagr…

2024上半年教资笔试报名详细教程1月12日开始报名啦

重点提醒&#xff1a; 1、注册开放时间&#xff1a;2024年1月10日开始。 &#xff08;参加过笔试的考生&#xff0c;需要重新注册&#xff0c; 不影响已获得的笔试成绩。名额少的考点建议提前注册抢名额&#xff09; 2、网上报名时间&#xff1a;2024年1月12日至15日。 千万不…

[开发语言][c++][python]:C++与Python中的赋值、浅拷贝与深拷贝

C与Python中的赋值、浅拷贝与深拷贝 1. Python中的赋值、浅拷贝、深拷贝2. C中的赋值、浅拷贝、深拷贝2.1 概念2.2 示例&#xff1a;从例子中理解1) 不可变对象的赋值、深拷贝、浅拷贝2) 可变对象的赋值、浅拷贝与深拷贝3) **可变对象深浅拷贝(外层、内层改变元素)** 写在前面&…

资源三角形

美国哈佛大学的研究小组提出了著名的资源三角形&#xff1a;没有物质&#xff0c;什么也不存在&#xff1b;没有能量&#xff0c;什么也不会发生&#xff1b;没有信息&#xff0c;任何事物都没有意义。物质、能量和信息是相互有区别的&#xff0c;是人类社会赖以生存、发展的三…

Nginx负载均衡以及常用的7层协议和4层协议的介绍

一、引言 明人不说暗话&#xff0c;下面来解析一下 Nginx 的负载均衡。需要有 Linux 和 Nginx 环境哈。 二、nginx负载均衡的作用 高并发&#xff1a;负载均衡通过算法调整负载&#xff0c;尽力均匀的分配应用集群中各节点的工作量&#xff0c;以此提高应用集群的并发处理能力…

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

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

【算法与数据结构】62、LeetCode不同路径

文章目录 一、题目二、解法2.1 动态规划解法2.2 数论解法 三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 2.1 动态规划解法 思路分析&#xff1a;机器人只能向下或者向右移动&#xff0c;那么到达&a…

leetcode 2645. 构造有效字符串的最少插入数-python

题目&#xff1a; 给你一个字符串 word &#xff0c;你可以向其中任何位置插入 “a”、“b” 或 “c” 任意次&#xff0c;返回使 word 有效 需要插入的最少字母数。 如果字符串可以由 “abc” 串联多次得到&#xff0c;则认为该字符串 有效 。 解题方法 1.先判断字符串是否…

【剪枝】【广度优先】【深度优先】488祖玛游戏

作者推荐 【动态规划】458:可怜的小猪 涉及知识点 剪枝 广度优先 深度优先 488祖玛游戏 在这个祖玛游戏变体中&#xff0c;桌面上有 一排 彩球&#xff0c;每个球的颜色可能是&#xff1a;红色 ‘R’、黄色 ‘Y’、蓝色 ‘B’、绿色 ‘G’ 或白色 ‘W’ 。你的手中也有一些…

kubeadm安装kubernetes

基本环境配置 节点分为&#xff1a;master&#xff0c;node&#xff0c;masterlb(keepalived虚拟Ip&#xff0c;不占用机器) k8s-master01 16 k8s-node01 113 15 k8s-node02 115 进入之后直接选done done 上海 123456 设置静态ip 然后去虚拟机里面设置ens即可 查看命…

MFC为对话框资源添加类

VC6新建一个对话框类型的工程; 建立之后资源中默认有2个对话框,一个是主对话框,About这个是默认建立的关于版权信息的; 然后主对话框有对应的.h和.cpp文件;可以在其中进行编程; 默认建立的有一个 关于 对话框; 在资源中新插入一个对话框,IDD_DIALOG1是对话框ID; 新加…

国际版WPS Office 18.6.1

【应用名称】&#xff1a;WPS Office 【适用平台】&#xff1a;#Android 【软件标签】&#xff1a;#WPS 【应用版本】&#xff1a;18.6.1 【应用大小】&#xff1a;160MB 【软件说明】&#xff1a;软件日常更新。WPS Office是使用人数最多的移动办公软件。独有手机阅读模式…

LLM主流框架:Causal Decoder、Prefix Decoder和Encoder-Decoder

本文将介绍如下内容&#xff1a; transformer中的mask机制Causal DecoderPrefix DecoderEncoder Decoder总结 一、transformer中的mask机制 在Transformer模型中&#xff0c;mask机制是一种用于在self-attention中的技术&#xff0c;用以控制不同token之间的注意力交互。具体…

探索WPF控件内容模型的四大支柱

WPF 内容模型 WPF控件内容模型主要指派生于System.Windows.Controls.Control类的各种控件&#xff0c;有四个可包含任意内容的类。 下表列出了继承自 Control 的类。 ContentControl&#xff1a;用于包含一段任意类型的内容。但是只能包含一个子元素作为其“内容”。它可以包…

图像分割deeplab系列

DeepLab系列是谷歌团队提出的一系列语义分割算法。DeepLab v1于2014年推出&#xff0c;并在PASCAL VOC2012数据集上取得了分割任务第二名的成绩&#xff0c;随后2017到2018年又相继推出了DeepLab v2&#xff0c;DeepLab v3以及DeepLab v3。DeepLab v1的两个创新点是空洞卷积&am…

【Python】新鲜出炉的海洋捕食者算法Python版本

2020年发表的海洋捕食者算法《Marine Predators Algorithm: A nature-inspired metaheuristic》。 作者只在原论文中给出了MATLAB代码&#xff0c;网上也没有Python版本&#xff0c;我自己用Python重写了MATLAB代码。 """2020海洋捕食者算法 """…

spaceship

通过数字平台启动您的网站、想法和未来&#xff0c;该平台旨在提供和连接您所需的域、托管、电子邮件和 Web 工具&#xff0c;并让您完全掌控 如果需要购买可以开5347的卡&#xff0c;点击获取

小学信息科技Python课程第2课:坐标与画笔

一、turtle画布与坐标系 在同一平面互相垂直且有公共原点的两条数轴构成平面直角坐标系。在坐标系中&#xff0c;水平方向的轴都称为x轴&#xff0c;垂直方向的轴都称为y轴 它们相交于O点&#xff0c;在这一个点里&#xff0c;x轴的值为0&#xff0c;y轴的值也为0&#xff0c;所…