Leetcode循环队列(数组实现及链表实现)

news2024/12/22 9:41:19

这道题十分考验我们对队列的理解。

文章目录

      • 队列的介绍
      • 队列的实现
      • 进入正题
      • 数组的方法
      • 链表实现

队列的介绍

队列是一种只允许在一段进行插入,在另一端进行删除的数据操作的特殊线性结构,,因此决定了他具有先入先出的特点,其中进行插入操作的一段叫做队尾,出队列的一端叫做队头。

在这里插入图片描述

队列的实现

队列可以使用链表或者数组进行实现,对于这两种实现方法,使用链表实现效果更好一点,两个指针中front为链表的头,即队列的队头,出数据的话只需要找到front的下一个假设为pre,将front销毁,front置为pre即可,如果是用数组的结构的话,出队列在数组头上出数据,效率会很低。
链表实现队列代码如下
Queue.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>


typedef int QDataType;

typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;


typedef struct Queue
{
	QNode* head;//头
	QNode* tail;//尾
	int size;//大小
}Que;
void QueueInit(Que* pq);//初始化
void QueuePush(Que* pq,QDataType);//入队列
void QueueDestroy(Que* pq);//销毁
void QueuePop(Que* pq);//出队列
QDataType QueueFront(Que* pq);//队头的数据
QDataType QueueBack(Que* pq);//队尾的数据

bool QueueEmpty(Que* pq);//检查是否为空
int QueueSize(Que* pq);//队列元素

Queue.c

#define _CRT_SECURE_NO_WARNINGS
#include "Queueh.h"

void QueueInit(Que* pq)
{
	assert(pq);

	pq->head = pq->tail = NULL;
	pq->size = 0;
}

void QueueDestroy(Que* pq)
{
	assert(pq);

	QNode* cur = pq->head;
	while (cur != NULL)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
	pq->size = 0;

}

void QueuePush(Que* pq, QDataType x)
{
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	newnode->next = NULL;
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->data = x;

	if (pq->tail == NULL)//这里要先进行判断
	{
		pq->head = pq->tail = newnode;//如果队列里没有一个元素
		//那么head和tail都为空,将newnode设置为队头,当然也是队尾,毕竟只有一个元素。
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
	pq->size++;
}

void QueuePop(Que* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));//判空,避免引起不必要的错误。
	if (pq->head->next != NULL)
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
	else
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	pq->size--;
}

QDataType QueueFront(Que* pq)
{
	assert(pq);
	//判断是否为空
	assert(!QueueEmpty(pq));
	return pq->head->data;//队头位置
}

QDataType QueueBack(Que* pq)
{
	assert(pq);
	//判断是否为空
	assert(!QueueEmpty(pq));
	return pq->tail->data;//队尾位置
}

bool QueueEmpty(Que* pq)//判断是否为空,为空则返回true
{
	if (pq->head == NULL)
	{
		return true;
	}
	else
	{
		return false;
	}
}

int QueueSize(Que* pq)
{
	assert(pq);
	return pq->size;
}

进入正题

链接:设计循环队列
在这里插入图片描述

前边的队列如果空间不够就会扩容,但是这里的循环队列大小是固定的,只可以保存k个元素,当然还是遵循先入先出的规则

 例如下边的环形队列,在pop掉队头数据后,这块空间不会被销毁的,可以继续存储值覆盖原来的值,假设k等于5,当入6个元素后,这个循环队列就满了,当出队列时,此时这个队列的首位置就可以继续存储数据。
在这里插入图片描述

数组的方法

结构体声明如下

typedef struct {
    int* a;
    int front;
    int rear;//尾
    int k;
} MyCircularQueue;

初始化函数如下

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue*obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //多开一个方便区分空和满
    obj->a=(int*)malloc(sizeof(int)*(k+1));
    obj->k=k;
    obj->front=obj->rear=0;
    return obj;
}

在这里插入图片描述
数组也面临一个同样的问题,如何判断是满还是空?
可以多开一块空间。k==5,开6块空间,最后一块空间浪费不用。
在这里插入图片描述
如果front等于tail就为空,在这里设置tail的值为0~5(要注意下标和位置有减一的关系),如果tail的下一个位置为front时,表示队列已满。

obj->tail%=(obj->k+1);//obj为循环队列变量

控制tail的值的变化范围,当tail等于6时置tail为0。
当然,在出队列过程中,front是会不断变化的。
我们看front变化的情况
pop一个数据后,向后移一位
在这里插入图片描述

判断是否队列已满的条件判断句如下

(obj->tail+1)%(obj->k+1 )==obj->front

前边已经说过了,tail的变化范围为0~5,此时tail被置为0,但front为1,不相等,就表示还有空余的位置,队列没有满,所以上边的判断语句在任何场景都是正常使用的。

判断是否已满函数如下(题目中tail被rear替换)

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return (obj->rear+1)%(obj->k+1  )==obj->front;
}

返回类型为布尔值,如果相等就返回true,不相等就返回false。
判断是否为空很简单,直接比较rear和front的值是否相等即可。

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

置空函数

void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->a);
    free(obj);
}

获取队头元素
很简单,返回front位置的元素即可

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

获取队尾元素
这里就要思考一下了
如果rear不在数组第一个空间上,直接返回数组rear-1处的值即可,当rear位于数组首元素,就要返回数组第k个元素。
代码如下

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        if(obj->rear==0)
        {
            return obj->a[obj->k];
        }
        else{
            return obj->a[obj->rear-1];
        }
		//下边是综合写法
        //return obj->a[(obj->rear+obj->k)%(obj->k+1)];
    }
}

出队列deQueue()
出队列只需要将front++即可,就算之前的数据不销毁,下次入队列操作也会覆盖他的数据。
代码如下

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

注意是要有返回值的,如果队列为空,就没有出数据,返回false,出数据成功就返回true。
入队列
代码如下:

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }

    obj->a[obj->rear]=value;
    obj->rear++;

    obj->rear%=(obj->k+1);

    return true;
}

首先判断循环队列是否已满,如果已满就返回false,如果没有满就将rear位置的值改为value,然后rear++,判断是否超出范围,如果超出就置为0。最后入队列成功返回true。
 到了这里这道题目就顺利解决了,如果使用双向链表来做这道题的话当然也可以,但是会稍微麻烦一点,有兴趣可以尝试尝试。
今天的内容就到这里啦,如果有错误欢迎各位指正哦!
综合上述代码后即可通过本题。
在这里插入图片描述

链表实现

链表实现就较为简单了,思路和前边数组实现的思路很像。
首先声明结构体

typedef struct listNode
{
    struct listNode* next;
    int val;
}LS;

typedef struct {
    LS* head;
    LS* tail;
    int size;
    int k;
} MyCircularQueue;

节点的结构体保存下一个结构体的指针,并且存储值val。
循环链表结构体有头结点,尾节点,size是队列中元素的个数,k为设定的循环队列的容量。
入队列时,获取一个初始化后新节点,并把之前的尾结点与这个节点连接起来。
获取新节点的函数如下

LS* buylistNode(int x)
{
    LS*listnode =(LS*)malloc(sizeof(LS));
    listnode ->next=NULL;
    listnode ->val=x;
    return listnode;
}

函数返回新造的节点。以供入队列函数使用。


至于判空判满函数我想就不用过多介绍,重要的是入队列和出队列。
出队列只需要头结点向后移,然后size–就好。

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(obj->head==NULL)
    {
        return false;
    }
    else
    {
        obj->head=obj->head->next;
        obj->size--;
        return true;
    }
}

入队列

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))//如果已满,就返回false
    {
        return false;
    }
    LS*node=buylistNode(value);
    if(obj->head==NULL)//第一次插入
    {
        obj->head=node;
        obj->tail=node;
    }
    else
    {
        LS*tran=obj->tail;
        obj->tail=node;
        tran->next=obj->tail;
    }
    obj->size++;//注意size++
    return true;
}

全部代码如下

typedef struct listNode
{
    struct listNode* next;
    int val;
}LS;

typedef struct {
    LS* head;
    LS* tail;
    int size;
    int k;
} MyCircularQueue;

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

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return obj->size==obj->k;
}
MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue*obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    obj->head=NULL;
    obj->tail=NULL;
    obj->size=0;
    obj->k=k;
    return obj;
}

LS* buylistNode(int x)
{
    LS*listnode =(LS*)malloc(sizeof(LS));
    listnode ->next=NULL;
    listnode ->val=x;
    return listnode;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }
    LS*node=buylistNode(value);
    if(obj->head==NULL)
    {
        obj->head=node;
        obj->tail=node;
    }
    else
    {
        LS*tran=obj->tail;
        obj->tail=node;
        tran->next=obj->tail;
    }
    obj->size++;
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(obj->head==NULL)
    {
        return false;
    }
    else
    {
        obj->head=obj->head->next;
        obj->size--;
        return true;
    }
}

int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->head->val;
}

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->tail->val;
}

void myCircularQueueFree(MyCircularQueue* obj) {
    while(obj->head!=NULL)
    {
        LS*a=obj->head->next;
        free(obj->head);
        obj->head=a;
    }
    free(obj);
}

这篇文章到这里就结束啦,如果有问题欢迎大家指出。

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

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

相关文章

2023软件测试高薪必备pytest-yaml 测试平台-1.新增项目和用例

前言 基于pytest-yaml-yoyo 框架写的接口测试平台&#xff0c;在web网页平台上维护yaml测试用例&#xff0c;执行用例&#xff0c;并查看allure报告。 pytest-yaml-yoyo 框架简介 https://gitee.com/yoyoketang/pytest-yaml-yoyo 本框架只需pip安装即可上手 pip install pyt…

C++类和对象万字详解(典藏版)

文章目录 前言认识类和对象使用 struct 定义类class 定义类类的声明和定义分离类大小的计算this指针this指针的常见的面试题 构造函数与构析函数构造函数初始化列表 构析函数默认生成的构造函数和构析函数 拷贝构造函数默认类型转化与 explicit 关键字 static 成员变量运算符重…

判断字符串是否为json

//营业时间返回数组String businessDate merchantInfoResp.getBusinessDate();Object obj JSON.parse(businessDate);if (obj instanceof JSONArray) {merchantInfoResp.setBusinessDateDesc(JSON.parseArray(JSON.toJSONString(obj), Integer.class));} else {//营业日期判断…

YB2408CAX系列是一款高效、高频的同步降压DC-DC转换器,最高可带载2A连续电流。

概述: YB2408CAX系列是一款高效、高频的同步降压DC-DC转换器&#xff0c;最高可带载2A连续电流。YB2408CAX系列可在2.7V到5.5V的宽输入电源电压下工作。内部的主开关和同步开关管的RoSO非常小&#xff0c;从而传导损耗很小&#xff0c;效率很高。该款芯片工作开关频率为1.5MHz…

向中学生郑重推荐电视剧《狂花凋落》

笔者为什么要向网友&#xff0c;特别是要向中学生网友&#xff0c;郑重推荐能真实反应当年“文革”时期知识青年上山下乡悲惨遭遇的电视剧《狂花凋落》&#xff0c;因为如果真的再来上一次那种亘古未有的人间浩劫&#xff0c;那么在蜜糖里泡大、丁点儿苦都吃不下的当今中学生&a…

操作系统备考学习 day12 (第五章)

操作系统备考学习 day12 第五章 &#xff08;输入/输出&#xff09;I/O管理5.1 I/O管理概述5.1.1 I/O设备I/O设备的分类 5.1.2 I/O控制器I/O设备的电子部件 5.1.3 I/O控制方式程序直接控制方式中断驱动方式DMA方式DMA控制器通道控制方式 5.1.4 I/O软件层次结构用户层软件设备独…

直播电商大变局:店播时代终于来了!

店播时代终于来了。 直播在2023年双十一的亮点&#xff0c;也是焦点。今年双十一&#xff0c;舆论场的注意力都集中在了几大平台的头部主播身上&#xff0c;却少有人注意店播的表现——根据淘宝直播官方数据&#xff0c;10月31日淘宝直播上有29个直播间开局即破亿&#xff0c;…

Pytorch从零开始实战08

Pytorch从零开始实战——YOLOv5-C3模块实现 本系列来源于365天深度学习训练营 原作者K同学 文章目录 Pytorch从零开始实战——YOLOv5-C3模块实现环境准备数据集模型选择开始训练可视化模型预测总结 环境准备 本文基于Jupyter notebook&#xff0c;使用Python3.8&#xff0c…

应用层中一些零碎且易忘的知识点

邮件发送协议&#xff1a; SMTP&#xff1a;SMTP协议只能传送ASCII码文本数据&#xff0c;不能传送可执行文件或其他的二进制对象&#xff08;如带有图片、音频或视频数据的多媒体邮件&#xff09;MIMP&#xff1a;为解决SMTP传送非ASCII码文本的问题&#xff0c;提出了多用途因…

HR模块开发(1):简单的开发流程和注意事项

HR模块开发 一、模块概述 人力资源管理解决方案关注3个领域:每位雇员都发展和维护着‘公司内’和‘公司外’的种种‘关系’。运用科技,强化这些关系,可以提高忠诚度和生产力,公司整体得到商业价值。 员工关系管理员工职业生命周期管理员工事务处理管理HR模块的基本知识和构…

Linux: MV指令(覆盖替换重命名)

MV指令 mv [-bfiuv] [–help] [–version] [-S <附加字尾>] [-V <方法>] [源文件或目录] [目标文件或目录]-b 若需覆盖文件&#xff0c;则覆盖前先行备份-f 若目标文件或目录与现有的文件或目录重复&#xff0c;则直接覆盖现有的文件或目录-i 覆盖前先行询问用户–…

Python武器库开发-常用模块之copy模块(十五)

常用模块之copy模块(十五) 在Python编程中&#xff0c;我们经常遇到需要复制或拷贝数据的情况。为了避免不必要的问题和错误&#xff0c;Python提供了copy模块来处理复制操作。本文将介绍copy模块的用法&#xff0c;包括浅拷贝和深拷贝的概念以及如何在不同场景中使用。 我们…

【有源码】基于Python的篮球人才管理系统Springboot的篮球竞赛管理系统(源码、调试、lw、开题报告、ppt)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人七年开发经验&#xff0c;擅长Java、Python、PHP、.NET、微信小程序、爬虫、大数据等&#xff0c;大家有这一块的问题可以一起交流&#xff01; &#x1f495;&…

算法题:16. 最接近的三数之和(Python Java 详解)

解题思路 Step1&#xff1a;先对数组排序&#xff0c;然后设置3个指针&#xff0c;指针1遍历范围为&#xff08;0~数组长度减2&#xff09;。 Step2&#xff1a;指针1位置确定时&#xff0c;指针1后面的数组元素首位各放置一个指针&#xff08;指针2、指针3&#xff09;。 S…

006 Linux 进程的概念 | 获取进程的PID

前言 本文将会向您进程的概念&#xff0c;程序与进程的区别&#xff0c;如何获取进程的标识符-pid 文章重点 1.描述进程——PCB 进程与程序的区别 CPU对进程列表的处理 2.获取进程PID 描述进程-PCB 进程概念 课本概念&#xff1a;程序的一个执行实例或正在执行的程序 内核…

在Flask中实现文件上传七牛云中并下载

在Flask中实现文件上传和七牛云集成 文件上传是Web应用中常见的功能之一&#xff0c;而七牛云则提供了强大的云存储服务&#xff0c;使得文件存储和管理变得更加便捷。在本篇博客中&#xff0c;我们将学习如何在Flask应用中实现文件上传&#xff0c;并将上传的文件保存到七牛云…

Java NIO 编程

1. 简介 Java NIO 是 JDK 1.4 中引入的新的 IO 方式&#xff0c;它主要包含 Buffer、Channel、Selector 这三个核心的组件&#xff0c;它与传统 IO 的区别如下&#xff1a; NIO IO 面向缓冲 面向流 同步非阻塞 同步阻塞 多路复用&#xff08;选择器&#xff09; 无 1.1…

【自动控制原理】时域分析法:一阶、二阶、高阶系统的时间响应及动态性能

文章目录 第3章 时域分析法3.1 基本概念3.1.1 典型输入信号3.1.2 系统动态性能指标 3.2 一阶系统的时间响应及动态性能一阶系统的标准形式&#xff08;尾1标准型&#xff09;3.2.1一阶惯性环节的单位阶跃响应3.2.2一阶惯性环节的单位速度响应3.2.3一阶惯性环节的单位脉冲响应3.…

乌班图 Linux 系统 Ubuntu 23.10.1 发布更新镜像

Ubuntu 团队在其官网上发布了Ubuntu 23.10.1 版本,这是目前较新的 Ubuntu 23.10(Focal Fossa)操作系统系列的第一个发行版,旨在为社区提供最新的安装媒体。Ubuntu 22.04 LTS(Focal Fossa)操作系统系列于 2022 年 4 月 21 日发布。 Ubuntu 23.10 LTS(长期支持版本)可用…

Redis常见风险分析

击穿 概念&#xff1a;在Redis获取某一key时, 由于key不存在, 而必须向DB发起一次请求的行为, 称为“Redis击穿”。 引发击穿的原因&#xff1a; 第一次访问恶意访问不存在的keyKey过期 合理的规避方案&#xff1a; 服务器启动时, 提前写入规范key的命名, 通过中间件拦截对…