leetcode622.设计循环队列(C语言)

622.设计循环队列
622.设计循环队列

目录

一、题目链接和介绍

二、大体思路

三、具体步骤

四、代码部分

一、题目链接和介绍

622. 设计循环队列 - 力扣(LeetCode)

实现一个循环的队列,其特性队列的先进先出(FIFO)原则,队尾被连接在队首之后以形成一个循环,该题目即为实现其功能。

二、大体思路

①创建一个大小为k+1的数组。使用headtail两个变量来记录数组中数据的变化;

②存入数据时,tail指针向后移动;删除数据时,head指针向前移动。

③因为要存放k个数据,我们开辟k+1的空间的目的就是防止存入数据时发生了覆盖,当tail+1==head时即表示队列存满。

三、具体步骤

3.1 创建结构体

有一个head变量表示队列的头,tail表示队列的尾,k表示存入队列的大小,a开辟的数组。

typedef struct {
    int *a;
    int k;
    int head;
    int tail;
} MyCircularQueue;

3.2 队列的创建

因为结构体不好控制,而且下面题目的函数形参都是以结构体指针的形式传入,所以我们要初始创建时要使用结构体变量obj来控制。

又因为函数作用域的原因,所以我们创建该结构体时要使用malloc来开辟,这样函数销毁的时候我们创建的变量才不会被销毁,并且可以成功返回该结构体指针。

MyCircularQueue* myCircularQueueCreate(int k) {
        //控制该结构体(MyCircularQueue)的指针
        MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
        //开辟k+1的数组空间
        obj->a=(int*)malloc(sizeof(int)*(k+1));
        obj->head=0;
        obj->tail=0;
        obj->k=k;
        return obj;
}

3.3 队列的插入

我们的思路是,将数据插入到 a[tail] 的位置,然后使 tail++ ,这样就可以数据插入到队列中去。

这里有一个问题,因为我们是循环队列,如果出现这种情况:

head因为删除数据导致前移,然后插入数据时tail走出了数组。

 所以这时我们要判断,如果当tail==k+1时,我们就要让tail回归到对头。

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    //如果队列未满,直接返回false,插入失败
    if(myCircularQueueIsFull(obj))
        return false;
        obj->a[obj->tail]=value;
        obj->tail++;
        //obj->tail%=(k+1)

        if (obj->tail==obj->k+1)
        {
            obj->tail=0;
        }
        return true;
}

3.4删除数据

想删除数据,只用将head向前移动一格即可。当然,也可能出现以下这种情况。

所以我们同样可以判断,当head==k+1时,我们将head移动到对头。

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

3.5 队列的判空

我们的思路是,队列初始状态下head和tail都存的是同一个下标,即head==tail==0,且我们规定了tail+1==head才是存满的,所以说只有当head和tail相等时队列才为空

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

3.6 队列的判满

以下我们有两种形式判定队列的为满的情况

 

二:我们先来看第种情况,设置一个临时变量temp为tail+1,如果tamp==head就表示队列当前为满返回true。

种情况,设置一个临时变量temp为tail+1,如果temp==k+1,将temp置为0,再判断tamp是否等于head,如果等于,就表示为满,返回true

 

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    int next=obj->tail+1;
    //实际是再判断tail是否位于数组最后。
    if (next==obj->k+1)
    next=0;
    return next==obj->head;
}

3.7 队列头部数据

这就很简单了,直接返回head下标处的数据即可。

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

3.8 队列尾部数据

这里又分两种情况:①tail未被置0 ②tail被置零。

如果tail被置零,因为在程序的开始,我们就判断了队列是否为空,所以k位置处肯定是有数据的,我们直接返回k下标处的数据即可。

 

 

int myCircularQueueRear(MyCircularQueue* obj) {
       if (myCircularQueueIsEmpty(obj))
        return -1;
       if (obj->tail==0)
       {
           return obj->a[obj->k];
       }
        //return obj->a[(obj->tail+k)%(k+1)]
       return obj->a[obj->tail-1];
}

3.9 队列的销毁

我们首先要释放数组a,再释放队列。

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

四、代码部分

//使用数组实现
typedef struct {
    int *a;
    int k;
    int head;
    int tail;
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
        MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
        //开辟k+1的空间
        obj->a=(int*)malloc(sizeof(int)*(k+1));
        obj->head=0;
        obj->tail=0;
        obj->k=k;
        return obj;
}

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

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    int next=obj->tail+1;
    if (next==obj->k+1)
    next=0;
    return next==obj->head;
}


bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
        return false;
        obj->a[obj->tail]=value;
        obj->tail++;
        //obj->tail%=(k+1)

        if (obj->tail==obj->k+1)
        {
            obj->tail=0;
        }
        return true;
}

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

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

int myCircularQueueRear(MyCircularQueue* obj) {
       if (myCircularQueueIsEmpty(obj))
        return -1;
       if (obj->tail==0)
       {
           return obj->a[obj->k];
       }
        //return obj->a[(obj->tail+k)%(k+1)]
       return obj->a[obj->tail-1];
}



void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->a);
    free(obj);
}
<think>我们正在讨论队列数据结构的C语言实现。队列是一种先进先出(FIFO)的数据结构,通常使用数组或链表来实现。下面我将提供一个基于数组的循环队列的示例代码,并解释其关键部分。之后,我会提供一些相关问题。</think> ### C语言队列数据结构实现指南 以下是队列数据结构的关键实现要素及完整代码: #### ⚙️ 队列核心组成部分 1. **存储结构**:使用数组(定长)或链表(动态大小) 2. **基本操作**: - `enqueue()`: 入队操作 $O(1)$ - `dequeue()`: 出队操作 $O(1)$ - `isFull()`: 检查队列满 - `isEmpty()`: 检查队列空 #### 📜 基于数组的循环队列实现(固定容量) ```c #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #define MAX_SIZE 5 typedef struct { int front; // 队首索引 int rear; // 队尾索引 int size; // 当前元素数 int data[MAX_SIZE]; } Queue; // 初始化队列 Queue* createQueue() { Queue* q = (Queue*)malloc(sizeof(Queue)); q->front = 0; q->rear = -1; q->size = 0; return q; } // 检查队列是否为空 bool isEmpty(Queue* q) { return q->size == 0; } // 检查队列是否已满 bool isFull(Queue* q) { return q->size == MAX_SIZE; } // 入队操作 void enqueue(Queue* q, int value) { if (isFull(q)) { printf("队列已满!\n"); return; } q->rear = (q->rear + 1) % MAX_SIZE; // 循环使用空间 q->data[q->rear] = value; q->size++; } // 出队操作 int dequeue(Queue* q) { if (isEmpty(q)) { printf("队列为空!\n"); return -1; } int value = q->data[q->front]; q->front = (q->front + 1) % MAX_SIZE; // 循环移动队首 q->size--; return value; } // 查看队首元素 int peek(Queue* q) { if (isEmpty(q)) { printf("队列为空!\n"); return -1; } return q->data[q->front]; } // 测试用例 int main() { Queue* q = createQueue(); enqueue(q, 10); enqueue(q, 20); enqueue(q, 30); printf("队首元素: %d\n", peek(q)); // 10 printf("出队: %d\n", dequeue(q)); // 10 printf("出队: %d\n", dequeue(q)); // 20 enqueue(q, 40); enqueue(q, 50); enqueue(q, 60); printf("当前队列: "); while (!isEmpty(q)) { printf("%d ", dequeue(q)); // 30 40 50 60 } free(q); return 0; } ``` #### ⚡ 关键算法优化 1. **循环数组**:通过取模运算 `(index + 1) % MAX_SIZE` 实现空间复用 2. **双指针追踪**:`front` 和 `rear` 指针独立移动,避免数据搬迁 3. **动态检测**:`size` 计数器准确判断队列状态(空/满) #### 📚 推荐教程资源 1. [GeeksforGeeks队列教程](https://www.geeksforgeeks.org/queue-data-structure/) - 含动态数组实现和复杂度分析 2. [Programiz队列指南](https://www.programiz.com/dsa/queue) - 可视化演示操作过程 3. [Leetcode队列实战](https://leetcode.com/tag/queue/) - 实际应用问题集(需登录) #### 🔍 为什么使用循环队列? 传统数组实现队列存在"假溢出"问题:当队尾到达数组末端但前端仍有空位时无法入队。循环队列通过模运算将队列视为环形结构,使 $rear$ 和 $front$ 指针能循环移动到数组开头,空间利用率达到100%[^1]。 --- ### 相关问题 1. **队列与栈有什么本质区别?** 2. **如何实现动态扩容的链式队列?** 3. **循环队列中如何不依赖size计数器判断队空/队满?** 4. **优先级队列在C语言中如何实现?** 5. **队列在操作系统调度中有哪些具体应用?** [^1]: 循环队列的空间复用策略详解参见《数据结构算法分析》3.4章
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Brant_zero2022

素材免费分享不求打赏,只求关注

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值