循环队列的实现

ops/2024/10/16 4:29:48/

循环队列的意思就是用限定的内存空间来完成队列相关操作。在结构上是循环的,在物理上可以是链表或者是数组

那么我们首先从数组来实现循环队列:

数组型

数组的内存不是环形的,所以我们需要用到%来实现下标的循环。

那么循环队列里面最重要的就是判断我们这个队列还能不能增删。

这里我们判断能不能删很好判断,就是头指针和尾指针是否重合。但是我们判断是否能继续删呢?

我们发现依然是头尾指针重合。那么这就不好搞了

我们可以想到的两种解决方案:

1:我们定义一个size来判断长度

2:我们额外开辟一个内存,当我们将队列填满的时候,尾指针是在头指针的前面一格

两个都可以,这里我用后面的一种。

首先是建立结构体,分别是数组,尾指针,头指针,以及长度

typedef struct {

    int* arr;

    int begain;

    int tail;

    int size;

} MyCircularQueue;

其次是初始化不用多说

MyCircularQueue* myCircularQueueCreate(int pk) {

    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));

    if (!obj)return NULL;

    obj->size = pk;

    obj->arr= (int*)malloc(sizeof(int)*(pk+1));

    if (!obj->arr)return NULL;

    obj->begain = obj->tail = 0;

    return obj;

}

判断空的,上面讨论过

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {

    if (!obj||!obj->arr)return false;

    return obj->begain == obj->tail;

}

判满的,上面讨论过

bool myCircularQueueIsFull(MyCircularQueue* obj) {

    if (!obj)return false;

    return (obj->tail + 1) % (obj->size + 1) == obj->begain;

}

如队列的,入队列前要判断是否满了

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {

    if (!myCircularQueueIsFull(obj)) {

        obj->arr[obj->tail++] = value;

        obj->tail %= (obj->size + 1);

        return true;

    }

    else {

        return false;

    }

}

出队列函数,先判断是否为空

bool myCircularQueueDeQueue(MyCircularQueue* obj) {

    if (!myCircularQueueIsEmpty(obj)){

        obj->begain++;

        obj->begain%= obj->size + 1;

        return true;

    }

    else {

        return false;

    }

}

返回队列的头部元素

int myCircularQueueFront(MyCircularQueue* obj) {

    if(myCircularQueueIsEmpty(obj))return -1;

    return obj->arr[obj->begain];

}

返回队列尾部的元素

int myCircularQueueRear(MyCircularQueue* obj) {

    if(myCircularQueueIsEmpty(obj))return -1;

    int k = obj->tail == 0 ? obj->size : obj->tail-1;

    return obj->arr[k];

}

销毁队列,释放队列内存

void myCircularQueueFree(MyCircularQueue* obj) {

    free(obj->arr);

    free(obj);

}

也是过了

链表型

我们换一个链表来写写看:

首先链表虽然可以构成循环结构,但是我们如果要返回队列的尾部元素怎么办呢。

这里有两种解决方法:

一种是用双向链表。

一种是每次指向末尾的前一个节点。

这里我用第二种来实现一下:

先是结构体的创建

typedef struct listnode {
    int data;
    struct listnode* next;
}listnode;

typedef struct {
    listnode* head;
    listnode* tail;
} MyCircularQueue;

再是队列的创建。这里我们创建一个哨兵节点header来方便我们写代码,最后再释放掉


MyCircularQueue* myCircularQueueCreate(int k) {
    int num = k + 1;
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    if (!obj)return NULL;
    listnode* header = (listnode*)malloc(sizeof(listnode));
    if (!header)return NULL;
    obj->head = NULL;
    obj->tail = header;
    header->next = obj->head;
    
    while (num--) {
        listnode* cur = (listnode*)malloc(sizeof(listnode));
        if (!cur)return NULL;
        if (!obj->head)
            obj->head = cur;
        obj->tail->next = cur;
        obj->tail = obj->tail->next;
    }
    free(header);
    obj->tail->next = obj->head;
    return obj;
}

判断是否为空

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    if (!obj)return false;
    return obj->tail->next == obj->head;
}

判断是否满

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    if (!obj)return false;
    return obj->tail->next->next == obj->head;
}

push进队列,注意判断满

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if (myCircularQueueIsFull(obj))
        return false;
    else
    {
        obj->tail->next->data = value;
        obj->tail = obj->tail->next;
        return true;
    }
}

队列pop数据,注意判断空

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

返回队列开头的数据,注意判断空

int myCircularQueueFront(MyCircularQueue* obj) {
    if (!obj || myCircularQueueIsEmpty(obj)) {
        return -1;
    }
    else {
        return obj->head->data;
    }
}

同理

int myCircularQueueRear(MyCircularQueue* obj) {
    if (!obj || myCircularQueueIsEmpty(obj)) {
        return -1;
    }
    else {
        return obj->tail->data;
    }
}

释放内存就是一个链表的内存释放

void myCircularQueueFree(MyCircularQueue* obj) {
    if (!obj)return;
    if (obj->head->next = NULL) {
        free(obj->head);
        free(obj);
    }
    else {
        listnode* pnewptr = obj->head, * newptr = obj->head->next;
        while (newptr) {
            free(newptr);
            pnewptr = newptr;
            newptr = newptr->next;
        }
        free(pnewptr);
        free(obj);
    }
}


http://www.ppmy.cn/ops/42283.html

相关文章

如何在go项目中实现发送邮箱验证码、邮箱+验证码登录

前期准备 GoLand :2024.1.1 下载官网:https://www.jetbrains.com/zh-cn/go/download/other.html Postman: 下载官网:https://www.postman.com/downloads/ 效果图(使用Postman) Google: QQ: And …

sql server使用 SELECT INTO 进行数据表备份和创建临时中间表

在数据库操作中,常常需要将数据从一个表复制到另一个表,或将部分数据保存到一个新的表中进行进一步操作。SELECT INTO 是一个强大的 SQL 语句,可以在 SQL Server 和部分其他数据库系统中实现这一功能。本文将讨论如何使用 SELECT INTO 进行数…

卷积神经网络(CNN)详细介绍及其原理详解

卷积神经网络(Convolutional Neural Networks,简称CNN)是深度学习中非常重要的一类神经网络,主要用于图像识别、图像分类、物体检测等计算机视觉任务。本文将详细介绍卷积神经网络的基本概念、结构组成及其工作原理,并…

深度学习之基于Tensorflow的卷积神经网络手写数字识别系统

欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 手写数字识别是计算机视觉和模式识别领域的一个重要问题。传统的识别方法往往依赖于复杂的特征工程和…

溪谷联运SDK功能全面解析

近期,备受用户关注的手游联运10.0.0版本上线了,不少用户也选择了版本更新,其中也再次迎来了SDK的更新。溪谷软件和大家一起盘点一下溪谷SDK的功能都有哪些吧。 一、溪谷SDK具有完整的运营功能和高度扩展性 1.登录:登录是SDK最基础…

网关路由SpringCloudGateway、nacos配置管理(热更新、动态路由)

文章目录 前言一、网关路由二、SpringCloudGateway1. 路由过滤2. 网关登录校验2.1 鉴权2.2 网关过滤器2.3 登录校验2.3.1 JWT2.3.2 登录校验过滤器 3. 微服务从网关获取用户4. 微服务之间用户信息传递 三、nacos配置管理问题引入3.1 配置共享3.1.1 在Nacos中添加共享配置3.1.2 …

Algoriddim djay Pro Ai for Mac:AI引领,混音新篇章

当AI遇上音乐,会碰撞出怎样的火花?Algoriddim djay Pro Ai for Mac给出了答案。这款专业的DJ混音软件,以AI为引擎,引领我们进入混音的新篇章。 djay Pro Ai for Mac的智能混音功能,让每一位DJ都能感受到前所未有的创作…

App Store上的这款新应用可模拟38种不同的复古游戏平台

继PPSSPP、Delta 和Gamma 等模拟器在App Store首次亮相之后,苹果又完成了对另一款模拟应用程序的评估。广受欢迎的多平台模拟器 RetroArch 现已正式登陆 iPhone 和iPad,为玩家提供了一种在旅途中玩他们喜爱的传统游戏的方式。 备受期待的 RetroArch 模拟…