比特数据结构与算法(第三章_下)队列的概念和实现(力扣:225+232+622)

news2025/2/27 4:42:17

一、队列(Queue)

队列的概念:

① 队列只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。

入队列,进行插入操作的一端称为 队尾出队列,进行删除操作的一端称为 队头

③ 队列中的元素遵循先进先出的原则,即 FIFO 原则(First In First Out)

队列的结构:

二、队列的定义

链式队列

typedef int QueueDataType;   //队列类型
 
typedef struct QueueNode 
{
    struct QueueNode* next;  //指向下一个节点
    QueueDataType data;      //数据
} QueueNode;
 
typedef struct Queue 
{
    QueueNode* pHead;        //头指针
    QueueNode* pTail;        //尾指针
} Queue;

和栈不一样的是,栈使用数组和链表差别不大,只是数组更优。

队列使用数组就很麻烦了,使用使用链表。

为什么不使用单链表?

单链表我们只定义了一个指针指向头,没有定义尾指针。因为定义尾指针解决不了问题,比如尾插尾删。所以我们没有必要定义一个结构体把他们封到一起。这里我们再定义一个头指针 head 一个尾指针 tail ,这两个指针才有意义。因为根据队列的性质,我们只会在队尾插,不会再队尾删。所以这个尾指针的价值就得到了完美的体现,实际中定义几个指针是看你的需求确定的。

另外扩展了解一下,实际中我们有时还会使用一种队列叫循环队列。

如操作系统课程讲解生产者消费者模型时可以就会使用循环队列。

环形队列可以使用数组实现,也可以使用循环链表实现。

下面的力扣链接:622. 设计循环队列就是设计一种循环队列

三、队列的实现(完整代码)

Queue.h

#pragma once

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

typedef int QueueDataType;   //队列类型

typedef struct QueueNode 
{
    struct QueueNode* next;  //指向下一个节点
    QueueDataType data;      //数据
} QueueNode;

typedef struct Queue//如果不设置成结构体就需要传二级指针,还要传两个参数
{
    QueueNode* pHead;
    QueueNode* pTail;
} Queue;

void QueueInit(Queue* pQ);//初始化队列
void QueueDestroy(Queue* pQ);//销毁队列
bool QueueEmpty(Queue* pQ);//判断队列是否为空
void QueuePush(Queue* pQ, QueueDataType x);//入队
void QueuePop(Queue* pQ);//出队
QueueDataType QueueFront(Queue* pQ);//返回队头数据
QueueDataType QueueBack(Queue* pQ);//返回队尾数据
int QueueSize(Queue* pQ);//返回队列大小

Queue.c

#include "Queue.h"

void QueueInit(Queue* pQ) 
{
    assert(pQ);

    pQ->pHead = pQ->pTail = NULL;
}

void QueueDestroy(Queue* pQ) 
{
    assert(pQ); 

    QueueNode* cur = pQ->pHead;
    while (cur != NULL) 
    {
        QueueNode* curNext = cur->next;  //防止释放cur后找不到其下一个节点
        free(cur);                     
        cur = curNext;                   
    }
    pQ->pHead = pQ->pTail = NULL; 
}

bool QueueEmpty(Queue* pQ) 
{
    assert(pQ); 

    return pQ->pHead == NULL;//如果成立则为True,不成立则为False
}

//入队:队尾入数据,队头出(删)数据。如果是第一个入队的(队列为空)则既要当头又当尾
void QueuePush(Queue* pQ, QueueDataType x) 
{
    assert(pQ);

    QueueNode* new_node = (QueueNode*)malloc(sizeof(QueueNode));
    if (new_node == NULL)
    {
        printf("malloc failed!\n");
        exit(-1);
    }
    new_node->data = x;     //待插入的数据
    new_node->next = NULL;  //新的数据指向空

    if (pQ->pHead == NULL)//情况1: 队列为空
    {           
        pQ->pHead = pQ->pTail = new_node;
    }
    else //情况2: 队列不为空   队尾入数据
    {                              
        pQ->pTail->next = new_node;  //在现有尾的后一个节点放置new_node
        pQ->pTail = new_node;        //更新pTail,使它指向新的尾
    }
}

void QueuePop(Queue* pQ) // 出队:队尾入数据,队头出(删)数据
{
    assert(pQ);                          
    assert(!QueueEmpty(pQ));  

    QueueNode* headNext = pQ->pHead->next; //队头出数据,防止释放pHead后找不到其下一个节点
    free(pQ->pHead);
    pQ->pHead = headNext;                  //更新头 

    if (pQ->pHead == NULL)//删完之后 pHead == NULL 了 pTail 还是野指针
    {
        pQ->pTail = NULL;//处理一下尾指针,将尾指针置空
    }
}

QueueDataType QueueFront(Queue* pQ) //返回队头数据
{
    assert(pQ);                           
    assert(!QueueEmpty(pQ));    

    return pQ->pHead->data;
}

QueueDataType QueueBack(Queue* pQ) //返回队尾数据
{
    assert(pQ);
    assert(!QueueEmpty(pQ));

    return pQ->pTail->data;
}

int QueueSize(Queue* pQ) //返回队列大小
{
    assert(pQ);

    int count = 0;
    QueueNode* cur = pQ->pHead;
    while (cur != NULL) 
    {
        count++;
        cur = cur->next;
    }
    return count;
}

Test.c

#include "Queue.h"

void TestQueue1() 
{
    Queue q;
    QueueInit(&q);

    QueuePush(&q, 1);
    QueuePush(&q, 2);
    QueuePush(&q, 3);
    QueuePush(&q, 4);

    QueuePop(&q);
    QueuePop(&q);
    QueuePop(&q);
    QueuePop(&q);
    //QueuePop(&q);

    QueueDestroy(&q);
}

void TestQueue2() 
{
    Queue q;
    QueueInit(&q);

    QueuePush(&q, 1);
    QueuePush(&q, 2);
    //QueueDataType front = QueueFront(&q);
    //printf("%d ", front);
    //QueuePop(&q);  //pop掉去下一个

    QueuePush(&q, 3);
    QueuePush(&q, 4);

    //假设先入了1 2,让1出来,再继续入,它的顺序还是不会变。永远保持先进先出
    while (!QueueEmpty(&q)) 
    {
        QueueDataType front = QueueFront(&q);
        printf("%d ", front);
        QueuePop(&q);  //pop掉去下一个
    }
    printf("\n");

    QueueDestroy(&q);
}

int main(void) 
{
    //TestQueue1();
    TestQueue2();
    return 0;
}

、力扣OJ题

力扣链接:225. 用队列实现栈

难度简单

请你仅使用两个队列实现一个后入先出(LIFO)的栈,

并支持普通栈的全部四种操作(push、top、pop 和 empty)。

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。

  • int pop() 移除并返回栈顶元素。

  • int top() 返回栈顶元素。

  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

注意:

  • 你只能使用队列的基本操作 —— 也就是 push to back、peek/pop from front、size 和

  • is empty 这些操作。

  • 你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。

示例:
输入:
["MyStack", "push", "push", "top", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]
解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False

提示:

  • 1 <= x <= 9

  • 最多调用100 次 push、pop、top 和 empty

  • 每次调用 pop 和 top 都保证栈不为空

(选C给的代码:)

typedef struct {

} MyStack;


MyStack* myStackCreate() {

}

void myStackPush(MyStack* obj, int x) {

}

int myStackPop(MyStack* obj) {

}

int myStackTop(MyStack* obj) {

}

bool myStackEmpty(MyStack* obj) {

}

void myStackFree(MyStack* obj) {

}

/**
 * Your MyStack struct will be instantiated and called as such:
 * MyStack* obj = myStackCreate();
 * myStackPush(obj, x);
 
 * int param_2 = myStackPop(obj);
 
 * int param_3 = myStackTop(obj);
 
 * bool param_4 = myStackEmpty(obj);
 
 * myStackFree(obj);
*/

解析代码:

核心思路:

  1. 入数据,往不为空的队列入,保持另一个队列为空

  1. 出数据,依次出队头的数据,转移到另一个队列保存,只剩最后一个时Pop掉

和上一篇栈的OJ题一样,用C++写就很好写,但用C写就要自己创建 栈/队列

把我们刚才写的Queue.h和Queue.c复制粘贴过去后删掉头文件就行了

typedef int QueueDataType;   //队列类型

typedef struct QueueNode 
{
    struct QueueNode* next;  //指向下一个节点
    QueueDataType data;      //数据
} QueueNode;

typedef struct Queue//如果不设置成结构体就需要传二级指针,还要传两个参数
{
    QueueNode* pHead;
    QueueNode* pTail;
} Queue;

void QueueInit(Queue* pQ);//初始化队列
void QueueDestroy(Queue* pQ);//销毁队列
bool QueueEmpty(Queue* pQ);//判断队列是否为空
void QueuePush(Queue* pQ, QueueDataType x);//入队
void QueuePop(Queue* pQ);//出队(删数据)
QueueDataType QueueFront(Queue* pQ);//返回队头数据
QueueDataType QueueBack(Queue* pQ);//返回队尾数据
int QueueSize(Queue* pQ);//返回队列大小

void QueueInit(Queue* pQ) 
{
    assert(pQ);

    pQ->pHead = pQ->pTail = NULL;
}

void QueueDestroy(Queue* pQ) 
{
    assert(pQ); 

    QueueNode* cur = pQ->pHead;
    while (cur != NULL) 
    {
        QueueNode* curNext = cur->next;  //防止释放cur后找不到其下一个节点
        free(cur);                     
        cur = curNext;                   
    }
    pQ->pHead = pQ->pTail = NULL; 
}

bool QueueEmpty(Queue* pQ) 
{
    assert(pQ); 

    return pQ->pHead == NULL;//如果成立则为True,不成立则为False
}

//入队:队尾入数据,队头出(删)数据。如果是第一个入队的(队列为空)则既要当头又当尾
void QueuePush(Queue* pQ, QueueDataType x) 
{
    assert(pQ);

    QueueNode* new_node = (QueueNode*)malloc(sizeof(QueueNode));
    if (new_node == NULL)
    {
        printf("malloc failed!\n");
        exit(-1);
    }
    new_node->data = x;     //待插入的数据
    new_node->next = NULL;  //新的数据指向空

    if (pQ->pHead == NULL)//情况1: 队列为空
    {           
        pQ->pHead = pQ->pTail = new_node;
    }
    else //情况2: 队列不为空   队尾入数据
    {                              
        pQ->pTail->next = new_node;  //在现有尾的后一个节点放置new_node
        pQ->pTail = new_node;        //更新pTail,使它指向新的尾
    }
}

void QueuePop(Queue* pQ) // 出队:队尾入数据,队头出(删)数据
{
    assert(pQ);                          
    assert(!QueueEmpty(pQ));  

    QueueNode* headNext = pQ->pHead->next; //队头出数据,防止释放pHead后找不到其下一个节点
    free(pQ->pHead);
    pQ->pHead = headNext;                  //更新头 

    if (pQ->pHead == NULL)//删完之后 pHead == NULL 了 pTail 还是野指针
    {
        pQ->pTail = NULL;//处理一下尾指针,将尾指针置空
    }
}

QueueDataType QueueFront(Queue* pQ) //返回队头数据
{
    assert(pQ);                           
    assert(!QueueEmpty(pQ));    

    return pQ->pHead->data;
}

QueueDataType QueueBack(Queue* pQ) //返回队尾数据
{
    assert(pQ);
    assert(!QueueEmpty(pQ));

    return pQ->pTail->data;
}

int QueueSize(Queue* pQ) //返回队列大小
{
    assert(pQ);

    int count = 0;
    QueueNode* cur = pQ->pHead;
    while (cur != NULL) 
    {
        count++;
        cur = cur->next;
    }
    return count;
}

typedef struct {
    Queue q1;
    Queue q2;
} MyStack;


MyStack* myStackCreate() {
    MyStack* st = (MyStack*)malloc(sizeof(MyStack));
    QueueInit(&st->q1);//q1是结构体,还要取地址
    QueueInit(&st->q2);//q2是结构体,还要取地址
    return st;
}

void myStackPush(MyStack* obj, int x) {
    if (!QueueEmpty(&obj->q1))//q1不为空(入到q1)
    {
        QueuePush(&obj->q1, x);
    }
    else//q2不为空(入到q2),或者两个都为空(入到哪都行)
    {
        QueuePush(&obj->q2, x);
    }
}

int myStackPop(MyStack* obj) {
    //题目要求int pop() 移除并返回栈顶元素。
    //法一:
    //if (!QueueEmpty(&obj->q1))//q1不为空,转移到q2保存,只剩最后一个时Pop掉
    //{
    //    while (QueueSize(&obj->q1) > 1)
    //    {
    //        QueuePush(&obj->q2, QueueFront(&obj->q1));//从q1取数据Push到q2
    //        QueuePop(&obj->q1);
    //    }
    //    int ret = QueueFront(&obj->q1);
    //    QueuePop(&obj->q1);//题目要求int pop() 移除并返回栈顶元素。
    //    return ret;
    //}
    //else//q2不为空,转移到q1保存,只剩最后一个时Pop掉
    //{
    //    while (QueueSize(&obj->q2) > 1)
    //    {
    //        QueuePush(&obj->q1, QueueFront(&obj->q2));//从q2取数据Push到q1
    //        QueuePop(&obj->q2);
    //    }
    //    int ret = QueueFront(&obj->q2);
    //    QueuePop(&obj->q2);
    //    return ret;
    //}
    //法二:
    Queue* emptyQ = &obj->q1;//假设q1为空 q2非空
    Queue* nonemptyQ = &obj->q2;
    if (!QueueEmpty(&obj->q1))//假设失败,倒过来
    {
        emptyQ = &obj->q2;
        nonemptyQ = &obj->q1;
    }
    while (QueueSize(nonemptyQ) > 1)
    {
        QueuePush(emptyQ, QueueFront(nonemptyQ));//从非空队列取数据Push到空队列
        QueuePop(nonemptyQ);
    }
    int ret = QueueFront(nonemptyQ);
    QueuePop(nonemptyQ);
    return ret;
}

int myStackTop(MyStack* obj) {
    //题目要求int top() 返回栈顶元素。(队列尾)队列不能删尾,能取尾
    if (!QueueEmpty(&obj->q1))//q1不为空
    {
        return QueueBack(&obj->q1);
    }
    else//q2不为空
    {
        return QueueBack(&obj->q2);
    }
}

bool myStackEmpty(MyStack* obj) {
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

void myStackFree(MyStack* obj) {
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    free(obj);
}

力扣链接:232. 用栈实现队列

难度简单

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作

(push、pop、peek、empty):

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾

  • int pop() 从队列的开头移除并返回元素

  • int peek() 返回队列开头的元素

  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

说明:

  • 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。

  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

示例 1:
输入:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]
解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false

提示:

  • 1 <= x <= 9

  • 最多调用 100 次 push、pop、peek 和 empty

  • 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)

进阶:

  • 你能否实现每个操作均摊时间复杂度为 O(1) 的队列?换句话说,执行 n 个操作的总时间复杂度为 O(n) ,即使其中一个操作可能花费较长时间。

typedef struct {

} MyQueue;


MyQueue* myQueueCreate() {

}

void myQueuePush(MyQueue* obj, int x) {

}

int myQueuePop(MyQueue* obj) {

}

int myQueuePeek(MyQueue* obj) {

}

bool myQueueEmpty(MyQueue* obj) {

}

void myQueueFree(MyQueue* obj) {

}

/**
 * Your MyQueue struct will be instantiated and called as such:
 * MyQueue* obj = myQueueCreate();
 * myQueuePush(obj, x);
 
 * int param_2 = myQueuePop(obj);
 
 * int param_3 = myQueuePeek(obj);
 
 * bool param_4 = myQueueEmpty(obj);
 
 * myQueueFree(obj);
*/

解析代码:

我们知道,栈与队列的原理刚好相反,对于栈是【FILO】,对于队列是【FIFO】。

这就需要我们灵活地去使用这两种数据结构进行解题。对于本题, 因为需要使用栈来实现队列,

那还需要像我们上一题那样将两个队列倒来倒去实现一个出栈的操作吗?

答案是:不需要,对于这一题而言,我们需要有一个明确的思路,

也是去定义两个栈,一个栈专门入数据,一个栈专门出数据,具体的实现在下面

(和上题本质是一样的)

先把上篇写的栈复制粘贴过来(上篇OJ题那个更方便,char改回int)链接:

比特数据结构与算法(第三章_上)栈的概念和实现(力扣:20. 有效的括号)_GR C的博客-CSDN博客

typedef int StackDataType;
 
typedef struct Stack 
{
    StackDataType* array;  //数组
    int top;               //栈顶
    int capacity;          //容量
} Stack;
 
void StackInit(Stack* ps);
void StackDestroy(Stack* ps);
void StackPush(Stack* ps, StackDataType x);
bool StackEmpty(Stack* ps);
void StackPop(Stack* ps);
StackDataType StackTop(Stack* ps);
int StackSize(Stack* ps);
 
void StackInit(Stack* ps)//初始化
{
    assert(ps);
    ps->array = NULL;
    ps->top = 0;  // ps->top = -1
    ps->capacity = 0;
}
 
void StackDestroy(Stack* ps)//销毁
{
    assert(ps);
 
    free(ps->array);
    ps->array = NULL;
    ps->capacity = ps->top = 0;
}
 
void StackPush(Stack* ps, StackDataType x)//进栈
{
    assert(ps);
    if (ps->top == ps->capacity) 
    {
        int new_capacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
        StackDataType* tmp_arr =(StackDataType *) realloc(ps->array, sizeof(StackDataType) * new_capacity);
        if (tmp_arr == NULL) 
        {
            printf("realloc failed!\n");
            exit(-1);
        }
        // 更新
        ps->array = tmp_arr;
        ps->capacity = new_capacity;
    }
 
    ps->array[ps->top] = x;// 填入数据
    ps->top++;
}
 
bool StackEmpty(Stack* ps)//判断栈是否为空
{
    assert(ps);
 
    return ps->top == 0; //等于0就是空,就是真
}
 
void StackPop(Stack* ps)// 出栈
{
    assert(ps);
    //assert(ps->top > 0);  //防止top为空
    assert(!StackEmpty(ps));
 
    ps->top--;
}
 
StackDataType StackTop(Stack* ps)//返回栈顶数据
{
    assert(ps);
    //assert(ps->top > 0);  //防止top为空
    assert(!StackEmpty(ps));
 
    return ps->array[ps->top - 1];
}
 
int StackSize(Stack* ps) //计算栈的大小
{
    assert(ps);
 
    return ps->top;// 因为我们设定top是指向栈顶的下一个,所以top就是size
}

typedef struct {
    Stack pushST;
    Stack popST;
} MyQueue;


MyQueue* myQueueCreate() {
    MyQueue* q = (MyQueue*)malloc(sizeof(MyQueue));
    StackInit(&q->pushST);
    StackInit(&q->popST);

    return q;
}

void myQueuePush(MyQueue* obj, int x) {
    StackPush(&obj->pushST, x);
}

int myQueuePop(MyQueue* obj) {
    //int pop() 从队列的开头移除并返回元素
    //如果popST中没有数据,就把pushST的数据拿过去
    //这样popST的数据就符合先进先出了
    if (StackEmpty(&obj->popST))
    {
        while (!StackEmpty(&obj->pushST))
        {
            StackPush(&obj->popST, StackTop(&obj->pushST));
            StackPop(&obj->pushST);
        }
    }
    //有数据就直接出(删后返回)
    int ret = StackTop(&obj->popST);
    StackPop(&obj->popST);
    return ret;
}

int myQueuePeek(MyQueue* obj) {
    //int peek() 返回队列开头的元素
    //如果popST中没有数据,就把pushST的数据拿过去
    if (StackEmpty(&obj->popST))
    {
        while (!StackEmpty(&obj->pushST))
        {
            StackPush(&obj->popST, StackTop(&obj->pushST));
            StackPop(&obj->pushST);
        }
    }
    return StackTop(&obj->popST);//不删,直接取
}

bool myQueueEmpty(MyQueue* obj) {
    return StackEmpty(&obj->pushST) && StackEmpty(&obj->popST);
}

void myQueueFree(MyQueue* obj) {
    StackDestroy(&obj->pushST);
    StackDestroy(&obj->popST);
    free(obj);
}

/**
 * Your MyQueue struct will be instantiated and called as such:
 * MyQueue* obj = myQueueCreate();
 * myQueuePush(obj, x);
 
 * int param_2 = myQueuePop(obj);
 
 * int param_3 = myQueuePeek(obj);
 
 * bool param_4 = myQueueEmpty(obj);
 
 * myQueueFree(obj);
*/

力扣链接:622. 设计循环队列

难度中等

设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

你的实现应该支持如下操作:

  • MyCircularQueue(k): 构造器,设置队列长度为 k 。

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

  • Rear: 获取队尾元素。如果队列为空,返回 -1 。

  • enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。

  • deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。

  • isEmpty(): 检查循环队列是否为空。

  • isFull(): 检查循环队列是否已满。

示例:
MyCircularQueue circularQueue = new MyCircularQueue(3); // 设置长度为 3
circularQueue.enQueue(1); // 返回 true
circularQueue.enQueue(2); // 返回 true
circularQueue.enQueue(3); // 返回 true
circularQueue.enQueue(4); // 返回 false,队列已满
circularQueue.Rear(); // 返回 3
circularQueue.isFull(); // 返回 true
circularQueue.deQueue(); // 返回 true
circularQueue.enQueue(4); // 返回 true
circularQueue.Rear(); // 返回 4

提示:

  • 所有的值都在 0 至 1000 的范围内;

  • 操作数将在 1 至 1000 的范围内;

  • 请不要使用内置的队列库。

解析:

这道题用数组和链表都能实现,各有优劣

要注意下面的判空和判满问题:

【0】 【1】 【2】 【3】 【4】

一般情况tail+1等于front 如上面tail 是【1】,front是【2】(1+1)%(4+1)==2也是满

但是如果tail是【4】,front是【0】,tail+1等于front就不能判断

(4+1)%(4+1)==0 就是满

链表空的情况和上面一样,满的情况是这样:

数组实现 函数bool myCircularQueueDeQueue(MyCircularQueue* obj)的图:

数组实现的代码:(有空可以写写链表实现的)

typedef struct {
    //匿名结构体
    int* arr;
    int front;
    int tail;
    int k;
} MyCircularQueue;
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* cq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //OJ题一般都会开辟空间成功,不用判空了
    cq->arr = (int*)malloc(sizeof(int) * (k + 1));//初始化 开k+1个空间
    cq->front = cq->tail = 0;
    cq->k = k;
    return cq;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    //enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
    //(失败返回假)满了就失败  
    //先滑到下面实现myCircularQueueIsFull和myCircularQueueIsEmpty,记得在上面声明
    if (myCircularQueueIsFull(obj))
    {
        return false;
    }
    obj->arr[obj->tail] = value;
    obj->tail++;
    obj->tail %= (obj->k + 1);//超出k+1就模到0从头开始,没超,模和没模一样
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    //deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
    if (myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    obj->front++;  //不理解可以看上面的图
    obj->front %= (obj->k + 1); //超出k+1就模到0从头开始,没超,模和没模一样
    return true;
}

int myCircularQueueFront(MyCircularQueue* obj) {
    //Front: 从队首获取元素。如果队列为空,返回 -1 。
    if (myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->arr[obj->front];
}

int myCircularQueueRear(MyCircularQueue* obj) {
    //Rear: 获取队尾元素。如果队列为空,返回 -1 。
    if (myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    if (obj->tail == 0)//如果tail在0上,返回tail的上一个就是k
    {
        return obj->arr[obj->k];
    }
    else
    {
        return obj->arr[obj->tail - 1];
    }
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->front == obj->tail; //看上面判空和判满条件
}

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

void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->arr);
    free(obj);      //有两层  结构体里面有个数组
}

第三章:栈和队列 完。

下一章:第四章:树和二叉树

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

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

相关文章

小公司“混”的3年,我认真做了5件事,真的受益终生

小公司“混”的3年&#xff0c;我认真做了5件事&#xff0c;真的受益终生 目录&#xff1a;导读 功能测试很重要但不值钱 自动化测试在小公司没市场&#xff0c;但是你得会 给自己的一些忠告 第一件事&#xff1a;分清阶段&#xff0c;制定计划 第二件事&#xff1a;梳理…

SQL零基础入门学习(三)

SQL零基础入门学习&#xff08;二&#xff09; SQL WHERE 子句 WHERE 子句用于提取那些满足指定条件的记录。 SQL WHERE 语法 SELECT column1, column2, ... FROM table_name WHERE condition;参数说明&#xff1a; column1, column2, …&#xff1a;要选择的字段名称&…

进程或线程终止是否会释放锁

线程锁的必要性比如一个多线程抢票程序&#xff0c;tickets作为临界资源&#xff0c;所有的线程都要对它进行判断ticket是否大于0&#xff0c;以及ticket–的操作。用ticket–操作举例&#xff0c;虽然他看起来是一行C语言的代码&#xff0c;但是实际上它的底层汇编经历了三个阶…

OSS Compass 开源指南针发布,剑指开源生态健康

估量有尺&#xff0c;开源有道。2 月 21 日&#xff0c;开源指南针 OSS Compass 发布会在北京顺利举行。OSS Compass 的发布&#xff0c;标志着我国首个开源生态健康评估平台正式诞生。发布会上介绍了 OSS Compass 的理论研究及实践成果&#xff0c;公布了 OSS Compass 开源社区…

图解操作系统

硬件结构 CPU是如何执行程序的&#xff1f; 图灵机的工作方式 图灵机的基本思想&#xff1a;用机器来模拟人们用纸笔进行数学运算的过程&#xff0c;还定义了由计算机的那些部分组成&#xff0c;程序又是如何执行的。 图灵机的基本组成如下&#xff1a; 有一条「纸带」&am…

【数据库】redis 配置文件与发布订阅

目录 配置文件 一&#xff0c;Units 二&#xff0c; INCLUDE 三&#xff0c;NETWORK 1&#xff0c; bind 2&#xff0c; tcp-backlog 3&#xff0c;timeout 4&#xff0c; tcp-keepalive 四&#xff0c;GENERAL 1&#xff0c;daemonize 2&#xff0c; pidfile 3&…

ESP32设备驱动-内置霍尔磁力传感器数据读取

内置霍尔磁力传感器数据读取 文章目录 内置霍尔磁力传感器数据读取1、ESP32霍尔磁力传感器介绍2、软件准备3、硬件准备4、读取霍尔磁力传感值5、运行结果ESP32开发板具有内置霍尔效应传感器,可检测周围磁场的变化。本文将介绍如何在Arduino IDE中读取ESP32霍尔效应传感器的数据…

【VUE3.0_CSS功能】

CSS功能组件css作用域深度选择器&#xff08;标签名空格:deep(标签名)&#xff09;插槽选择器&#xff08;:soltted(标签名)&#xff09;全局选择器&#xff08;:global(类名)&#xff09;动态CSS&#xff08;v-bind&#xff09;useCSSModule拓展知识&#xff1a;deep的写法组件…

iPhone更换电池和屏幕后提醒非原厂配件的操作办法

---开局一张图&#xff0c;内容全靠编系列&#xff01; 【图】 自从在iPhone系统iOS13开始支持原厂配件检测后&#xff0c;可以说苹果也动起了维修站商家利益的这块蛋糕。道理自然简单&#xff0c;卷嘛&#xff01;全球汽车行业也不是靠卖新车才赚钱的&#xff0c;各种交通事故…

MATLAB/Simulink 通信原理及仿真学习(二)

文章目录MATLAB/Simulink 通信原理及仿真学习&#xff08;二&#xff09;simulink仿真常用的Simulink库1. 信号源模块库2. 数序运算模块3. 信号输出模块库4.仿真搭建5.搭建自己的库6.S-函数编写MATLAB/Simulink 通信原理及仿真学习&#xff08;二&#xff09; simulink仿真 交…

resp连接redis服务器

修改redis的配置文件使得windows的图形界面客户端可以连接redis服务器 resp安装好以后&#xff0c;可以在linux端打开redis.conf中做以下操作&#xff0c;使得windows的图形界面客户端可以连接redis服务器 方法一&#xff1a; 1&#xff0c;在redis.conf文件中添加bind 在文件…

K8s中gRpc通信负载均衡失效

上篇文章在做 整合K8sSpringCloudK8sSpringBootgRpc 时&#xff0c;发现K8s中使用gRpc通信&#xff0c;负载均衡功能失效查了下gRpc的最佳实践&#xff0c;找到这里Load balancingSome load balancers dont work effectively with gRPC. L4 (transport) load balancers operate…

【JUC2022】第二章 多线程锁

【JUC2022】第二章 多线程锁 文章目录【JUC2022】第二章 多线程锁一、乐观锁与悲观锁1.悲观锁2.乐观锁二、八锁案例1.标准情况&#xff0c;有a、b两个线程&#xff0c;请问先打印邮件还是短信【结果&#xff1a;邮件】2.sendEmail方法中加入暂停3秒钟&#xff0c;请问先打印邮件…

筑基六层 —— 整型提升及实用调式技巧

目录 一.修炼必备 二. 整型提升 三.实用调式技巧 一.修炼必备 1.入门必备&#xff1a;VS2019社区版&#xff0c;下载地址&#xff1a;Visual Studio 较旧的下载 - 2019、2017、2015 和以前的版本 (microsoft.com) 2.趁手武器&#xff1a;印象笔记/有道云笔记 3.修炼秘籍&…

植物大战 仿函数——C++

容器适配器 容器适配器不支持迭代器。栈这个东西&#xff0c;让你随便去遍历&#xff0c;是不好的。他是遵循后进先出的。所以他提供了一个街头top取得栈顶数据。 仿函数 仿函数&#xff08;functor&#xff09;是C中一种重载了函数调用运算符&#xff08;operator()&#x…

HTTP安全与HTTPS协议

目录 Http协议的安全问题 常见的加密方式 防止窃听 单向散列函数 单向散列值的特点 加密与解密 对称加密与非对称加密 对称加密的密钥配送问题 密钥配送问题的解决 非对称加密 前言&#xff1a; 公钥与私钥 非对称加密过程 混合密码系统 前言&#xff1a; 混合…

vue3中使用jszip压缩文件

1、安装依赖 npm install jszip npm install file-saver --save 2、使用 <template><el-card class"mb15"><template #header><span>jszip</span></template><!-- 二维码容器 --><div id"qrCodeBox">&…

手把手教你,解决C盘分区不足,C盘怎么扩大磁盘分区

由于在磁盘分区中&#xff0c;C盘是很重要的一个磁盘&#xff0c;为了保证C盘有足够的磁盘分区。其中扩大C盘分区很常见的操作之一。那么&#xff0c;C盘怎么扩大磁盘分区&#xff1f;在本文中&#xff0c;易我小编将全面地讲解C盘合并分区的方法。 一、为什么C盘怎么扩大磁盘分…

通过openssl生成pfx证书

通过centos7上自带的openssl工具来生成。首先创建一个pfxcert目录。然后进入此目录。 1.生成.key文件&#xff08;内含被加密后的私钥&#xff09;&#xff0c;要求输入一个自定义的密码 [rootlocalhost cert]# openssl genrsa -des3 -out server.key 2048 Generating RSA priv…

Win10+VS2019+Qt5.15.2下编译QCAD

一&#xff1a;官方说法&#xff1a;WindowsDownload and install a C compiler, for example:Visual Studio C Express or Visual Studio CommunityDownload and install Qt from qt.io (see supported platforms):Download for example the online installer fileqt-opensour…