DS:循环队列的实现

                                                 创作不易,给个三连吧!! 

一、前言

对于循环队列,博主也是源自于一道力扣的OJ题

力扣:循环队列的设置

      后来我在网上查过,这个循环队列是有自己的应用场景的!!并不是出题者为了出题而产生的,所以我觉得不光要能做会这道题,还得多去探究这道题的不同方式。而且这道题虽然是循环队列,看似好像要把头和尾连起来,但实际上实现过程中是可以不需要的!这也是他非常特别的一点,因此在这我会重点介绍他的数组实现和链式结构实现。

二、数组实现循环队列

怎么用数组去实现循环队列呢?我们来画图研究一下:

2.1 结构体的创建

typedef int QDataType;
typedef struct MyCircularQueue
{  QDataType* a;//动态数组int capacity;//记录循环队列的容量int front;//记录队首int rear;//记录队尾
} MyCircularQueue;

2.2 构造一个k长度的队列并返回

根据我们之前的思路,我们要多创建一块空间!!

MyCircularQueue* myCircularQueueCreate(int k) 
{MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));if (obj == NULL){perror("malloc obj fail");exit(1);}obj->a = (QDataType*)malloc(sizeof(QDataType) * (k + 1));if (obj->a == NULL){perror("malloc obj->a fail");exit(1);}obj->front = obj->rear = 0;obj->capacity = k;return obj;
}

2.3 向循环队列插入一个元素。如果成功插入则返回真。 

我们要往循环队列中插入一个元素,那么首先必须确保队列不为满(后面会封装),那我们之前分析过队列不为满的情况是rear指针的下一个位置是front,但是我们要注意一个特殊情况,如下图:

bool myCircularQueueEnQueue(MyCircularQueue* obj, QDataType value)
{if (myCircularQueueIsFull(obj))return false;obj->a[obj->rear] = value;obj->rear++;obj->rear %= (obj->capacity + 1);return true;
}

 但是我们要注意的是实际上我们是多开了一个空间!!!%的时候要把多的空间算上

2.4 向循环队列删除一个元素。如果成功删除则返回真。

我们要往循环队列中删除一个元素,那么首先必须确保队列不为空(后面会封装),front++就行了,同样front也会遇到上面这种情况,处理当时一样,++完后%上数组的实际大小

bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{if (myCircularQueueIsEmpty(obj))return false;obj->front++;obj->front %= (obj->capacity + 1);return true;
}

2.5 从队首获取元素。如果队列为空,返回 - 1 

直接取头指针就行了

int myCircularQueueFront(MyCircularQueue* obj)
{if (myCircularQueueIsEmpty(obj))return -1;return obj->a[obj->front];
}

2.6 从队尾获取元素。如果队列为空,返回 - 1 

要注意rear指针指向的是最后一个元素的下一个位置,所以要取得的话就要找到rear的前一个位置的下标,这里我们不能直接让rear--,因为我们只是获取队列尾的元素,并不能去改变rear的指向,所以我们要算出rear前面那个位置的下标,其实也是一样,利用%的修正,让rear加上数组实际大小-1,然后再%数组的大小,就刚还是rear前面的位置的下标了!!

int myCircularQueueRear(MyCircularQueue* obj) 
{if (myCircularQueueIsEmpty(obj))return -1;return obj->a[(obj->rear + obj->capacity) % (obj->capacity + 1)];
}

2.7 判断循环队列是否为空

bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{return obj->front == obj->rear;
}

2.8 判断循环队列是否为满

bool myCircularQueueIsFull(MyCircularQueue* obj)
{return (obj->rear + 1)%(obj->capacity + 1) ==obj->front;//rear为k的时候正好
}

2.9 销毁循环队列

void myCircularQueueFree(MyCircularQueue* obj)
{free(obj->a);//没必要置空,因为obj用不了,obj->a也用不了  front rear k 也没必要释放free(obj);//obj = NULL;
}

2.10 全部代码

2.10.1 MyCircularQueue.h

#pragma once
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<assert.h>typedef int QDataType;
typedef struct MyCircularQueue
{  QDataType* a;//动态数组int capacity;//记录循环队列的容量int front;//记录队首int rear;//记录队尾
} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k);//构造一个k长度的队列
bool myCircularQueueEnQueue(MyCircularQueue* obj, QDataType value);// 向循环队列插入一个元素。如果成功插入则返回真。
bool myCircularQueueDeQueue(MyCircularQueue* obj);// 向循环队列删除一个元素。如果成功删除则返回真。
int myCircularQueueFront(MyCircularQueue* obj); //从队首获取元素。如果队列为空,返回 - 1 。
int myCircularQueueRear(MyCircularQueue* obj);//从队尾获取元素。如果队列为空,返回 - 1 。
bool myCircularQueueIsEmpty(MyCircularQueue* obj);//判断循环队列是否为空
bool myCircularQueueIsFull(MyCircularQueue* obj);//判断循环队列是否为满
void myCircularQueueFree(MyCircularQueue* obj);//销毁循环队列

 2.10.2 MyCircularQueue.c

#include"MyCircularQueue.h"MyCircularQueue* myCircularQueueCreate(int k) 
{MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));if (obj == NULL){perror("malloc obj fail");exit(1);}obj->a = (QDataType*)malloc(sizeof(QDataType) * (k + 1));if (obj->a == NULL){perror("malloc obj->a fail");exit(1);}obj->front = obj->rear = 0;obj->capacity = k;return obj;
}bool myCircularQueueEnQueue(MyCircularQueue* obj, QDataType value)
{if (myCircularQueueIsFull(obj))return false;obj->a[obj->rear] = value;obj->rear++;obj->rear %= (obj->capacity + 1);return true;
}bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{if (myCircularQueueIsEmpty(obj))return false;obj->front++;obj->front %= (obj->capacity + 1);return true;
}int myCircularQueueFront(MyCircularQueue* obj)
{if (myCircularQueueIsEmpty(obj))return -1;return obj->a[obj->front];
}int myCircularQueueRear(MyCircularQueue* obj) 
{if (myCircularQueueIsEmpty(obj))return -1;return obj->a[(obj->rear + obj->capacity) % (obj->capacity + 1)];
}bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{return obj->front == obj->rear;
}bool myCircularQueueIsFull(MyCircularQueue* obj)
{return (obj->rear + 1)%(obj->capacity + 1) ==obj->front;//rear为k的时候正好
}void myCircularQueueFree(MyCircularQueue* obj)
{free(obj->a);//没必要置空,因为obj用不了,obj->a也用不了  front rear k 也没必要释放free(obj);//obj = NULL;
}

2.11 相关选择题

5.现有一循环队列,其队头指针为front,队尾指针为rear;循环队列长度为N。其队内有效长度为?(假设队头不存放数据)( ?)
A (rear - front + N) % N + 1
B (rear - front + N) % N
C (rear - front) % (N + 1)
D (rear - front + N) % (N - 1)

答:这题就是根据我们上面那道题的一个变形,所以我们知道肯定是%上长度的,所以可以直接选B

三、链式结构实现循环队列

怎么用链式结构来实现循环队列呢?我们来分析一下:

3.1 结构体的创建

我们按照链式队列的思路,创建一个队列结构体来管理头尾指针,同时加上capacity(容量)和size(有效数据)

typedef int QDataType;
typedef struct QNode
{struct QNode* next;//节点QDataType val;//数据域
}QNode;typedef struct MyCircularQueue
{QNode* front;//链表的头指针QNode* rear;//链表的尾指针int capacity;//记录链表的容量int size;//记录链表的有效节点
}MyCircularQueue;

3.2 构造一个k长度的队列并返回

MyCircularQueue* myCircularQueueCreate(int k)
{MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));if (obj == NULL){perror("malloc fail");exit(1);}obj->front = obj->rear = NULL;obj->size = 0;obj->capacity = k;return obj;
}

3.3 向循环队列插入一个元素。如果成功插入则返回真。

bool myCircularQueueEnQueue(MyCircularQueue* obj, QDataType value)
{//如果为满了,直接就返回falseif (myCircularQueueIsFull(obj))return false;//不满足就创建节点QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc fail");exit(1);}newnode->val = value;newnode->next = NULL;//创建成功,要考虑队列为空和不为空的情况if (myCircularQueueIsEmpty(obj))//为空,让新节点成为头obj->front = obj->rear = newnode;else//不为空,让tail继续往后遍历{obj->rear->next = newnode;obj->rear = newnode;}obj->size++;return true;
}

3.4 向循环队列删除一个元素。如果成功删除则返回真。

bool myCircularQueueDeQueue(MyCircularQueue* obj)
{//为空就没有删的必要了if (myCircularQueueIsEmpty(obj))return false;//不为空,删除头节点,让下一个节点成为新的头,然后释放掉QNode* cur = obj->front->next;free(obj->front);obj->front = cur;obj->size--;return true;
}

3.5 从队首获取元素。如果队列为空,返回 - 1 

int myCircularQueueFront(MyCircularQueue* obj)
{//为空,返回1if (myCircularQueueIsEmpty(obj))return -1;//不为空,就获取头指针的数据return obj->front->val;
}

3.6 从队尾获取元素。如果队列为空,返回 - 1

int myCircularQueueRear(MyCircularQueue* obj)
{//为空,返回1if (myCircularQueueIsEmpty(obj))return -1;//不为空,就获取尾指针的数据return obj->rear->val;
}

3.7 判断循环队列是否为空

bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{return obj->size == 0;
}

3.8 判断循环队列是否为满

bool myCircularQueueIsFull(MyCircularQueue* obj)
{return obj->size == obj->capacity;
}

3.9 销毁循环队列

void myCircularQueueFree(MyCircularQueue* obj)
{//本质是链表,要一个个释放QNode* pcur = obj->front;//用来遍历删除的while (pcur){QNode* next= pcur->next;free(pcur);pcur = next;}free(obj);
}

3.10 全部代码

3.10.1 MyCircularQueue.h

#pragma once
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<assert.h>typedef int QDataType;
typedef struct QNode
{struct QNode* next;//节点QDataType val;//数据域
}QNode;typedef struct MyCircularQueue
{QNode* front;//链表的头指针QNode* rear;//链表的尾指针int capacity;//记录链表的容量int size;//记录链表的有效节点
}MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k);//构造一个k长度的队列
bool myCircularQueueEnQueue(MyCircularQueue* obj, QDataType value);// 向循环队列插入一个元素。如果成功插入则返回真。
bool myCircularQueueDeQueue(MyCircularQueue* obj);// 向循环队列删除一个元素。如果成功删除则返回真。
int myCircularQueueFront(MyCircularQueue* obj); //从队首获取元素。如果队列为空,返回 - 1 。
int myCircularQueueRear(MyCircularQueue* obj);//从队尾获取元素。如果队列为空,返回 - 1 。
bool myCircularQueueIsEmpty(MyCircularQueue* obj);//判断循环队列是否为空
bool myCircularQueueIsFull(MyCircularQueue* obj);//判断循环队列是否为满
void myCircularQueueFree(MyCircularQueue* obj);//销毁循环队列

3.10.2 MyCircularQueue.c

MyCircularQueue* myCircularQueueCreate(int k)
{MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));if (obj == NULL){perror("malloc fail");exit(1);}obj->front = obj->rear = NULL;obj->size = 0;obj->capacity = k;
}bool myCircularQueueEnQueue(MyCircularQueue* obj, QDataType value)
{//如果为满了,直接就返回falseif (myCircularQueueIsFull(obj))return false;//不满足就创建节点QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc fail");exit(1);}newnode->val = value;newnode->next = NULL;//创建成功,要考虑队列为空和不为空的情况if (myCircularQueueIsEmpty(obj))//为空,让新节点成为头obj->front = obj->rear = newnode;else//不为空,让tail继续往后遍历{obj->rear->next = newnode;obj->rear = newnode;}obj->size++;return true;
}bool myCircularQueueDeQueue(MyCircularQueue* obj)
{//为空就没有删的必要了if (myCircularQueueIsEmpty(obj))return false;//不为空,删除头节点,让下一个节点成为新的头,然后释放掉QNode* cur = obj->front->next;free(obj->front);obj->front = cur;obj->size--;return true;
}int myCircularQueueFront(MyCircularQueue* obj)
{//为空,返回1if (myCircularQueueIsEmpty(obj))return -1;//不为空,就获取头指针的数据return obj->front->val;
}int myCircularQueueRear(MyCircularQueue* obj)
{//为空,返回1if (myCircularQueueIsEmpty(obj))return -1;//不为空,就获取尾指针的数据return obj->rear->val;
}bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{return obj->size == 0;
}bool myCircularQueueIsFull(MyCircularQueue* obj)
{return obj->size == obj->capacity;
}void myCircularQueueFree(MyCircularQueue* obj)
{//本质是链表,要一个个释放QNode* pcur = obj->front;//用来遍历删除的while (pcur){QNode* next= pcur->next;free(pcur);pcur = next;}free(obj);
}

四、总结

        我们会发现,在这边无论是用数组实现和链表实现,本质上我们只是从逻辑层次上把它认为是相连的,但是物理层次上并没有把它连在一起,虽然链表是可以做到相连的,但是相连的话会比较复杂,不相连我们也可以解决,只要保证我们能够控制得住边界问题就行!! 

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

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

相关文章

【数据分享】2014-2024年全国监测站点的逐时空气质量数据(15个指标\Excel\Shp格式)

空气质量的好坏反映了空气的污染程度&#xff0c;在各项涉及城市环境的研究中&#xff0c;空气质量都是一个十分重要的指标。空气质量是依据空气中污染物浓度的高低来判断的。 我们发现学者王晓磊在自己的主页里面分享了2014年5月以来的全国范围的到站点的逐时的空气质量数据&…

Java+SpringBoot:农业疾病防治新选择

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

stm32 DCMI的知识点

1.DCMI的简介 DCMI全称Digital camera interface&#xff08;数字摄像头接口&#xff09;&#xff0c;是一种可以采集摄像头数据的一种接口。此接口适用于黑白摄像头、X24 和 X5 摄像头&#xff0c;并可以假定所有预处理&#xff08;如调整大小&#xff09;都可以在该摄像头模…

初识小程序

一、小程序的页面布局 1、小程序组件&#xff08;标签&#xff09; view&#xff1a;代表块级区块 html中的div text&#xff1a;代表行内区块 html中的span 2、样式选择器 --- 类选择器、标签选择器、后代选择器 3、组成页面的4种文件类型 .wxml&#xff1a;页面结构和内容 …

Ubuntu 20.04.1 共享samba给windows 10

通过ssh登录ubuntu&#xff0c;修改/etc/下的smb配置文件&#xff0c; uidq4932hzh57415u:/work$ cat /etc/samba/smb.conf [global] security ads realm V01.NET workgroup V01 idmap uid 10000-20000 idmap gid 10000-20000 winbind enum users yes winbind enum grou…

若依不分离版本部署流程

一、分离与不分离的区别 参考博客&#xff1a;前后端分离与不分离的本质区别&#xff01;_前后端分离本质-CSDN博客 概念适用场景前后端不分离前端页面看到的效果都是由后端控制&#xff0c;由后端渲染页面或重定向适合纯网页应用前后端分离后端仅返回前端所需的数据&#xf…

MySQL--SQL解析顺序

前言&#xff1a; 一直是想知道一条SQL语句是怎么被执行的&#xff0c;它执行的顺序是怎样的&#xff0c;然后查看总结各方资料&#xff0c;就有了下面这一篇博文了。 本文将从MySQL总体架构—>查询执行流程—>语句执行顺序来探讨一下其中的知识。 一、MySQL架构总览&a…

算法沉淀——BFS 解决拓扑排序(leetcode真题剖析)

算法沉淀——BFS 解决拓扑排序 01.课程表02.课程表 II03.火星词典 Breadth-First Search (BFS) 在拓扑排序中的应用主要是用来解决有向无环图&#xff08;DAG&#xff09;的拓扑排序问题。拓扑排序是对有向图中所有节点的一种线性排序&#xff0c;使得对于每一条有向边 (u, v)&…

Qt C++春晚刘谦魔术约瑟夫环问题的模拟程序

什么是约瑟夫环问题&#xff1f; 约瑟夫问题是个有名的问题&#xff1a;N个人围成一圈&#xff0c;从第一个开始报数&#xff0c;第M个将被杀掉&#xff0c;最后剩下一个&#xff0c;其余人都将被杀掉。例如N6&#xff0c;M5&#xff0c;被杀掉的顺序是&#xff1a;5&#xff…

Java实现自动化pdf打水印小项目 使用技术pdfbox、Documents4j

文章目录 前言源码获取一、需求说明二、 调研pdf处理工具word处理工具 三、技术栈选择四、功能实现实现效果详细功能介绍详细代码实现项目目录WordUtilsMain类实现部分&#xff1a;第一部分Main类实现部分&#xff1a;第二部分Main类实现部分&#xff1a;第三部分 资料获取 前言…

UE蓝图 分支(Branch)节点和源码

系列文章目录 UE蓝图 Cast节点和源码 UE蓝图 分支(Branch)节点和源码 文章目录 系列文章目录一、分支节点功能二、分支节点用法三、分支节点使用场景四、分支节点实现过程五、分支节点相关源码 一、分支节点功能 在Unreal Engine&#xff08;UE&#xff09;的蓝图中&#xff0…

数据采集三防平板丨三防平板电脑丨停车场应用

随着现代科技的不断发展&#xff0c;三防平板已经成为许多人工作和生活的必备工具。在停车场这个场景中&#xff0c;三防平板的应用可以大大提高停车场管理的效率和安全性。 停车场是现代城市交通管理的重要组成部分&#xff0c;它直接关系到城市交通的流畅和公共安全。停车场…