当前位置: 首页 > news >正文

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

一、队列(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_nodepQ->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_nodepQ->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 = -1ps->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);      //有两层  结构体里面有个数组
}

第三章:栈和队列 完。

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

相关文章:

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

一、队列&#xff08;Queue&#xff09;队列的概念&#xff1a;① 队列只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表。② 入队列&#xff0c;进行插入操作的一端称为 队尾。出队列&#xff0c;进行删除操作的一端称为 队头。③ 队列中的元素…...

c++提高篇——STL容器实现打分系统

一、案例说明 有5名选手:选手ABCDE&#xff0c;10个评委分别对每一名选手打分&#xff0c;去除最高分&#xff0c;去除评委中最低分&#xff0c;取平均分。 二、案例实现 在实现这个系统时&#xff0c;我们规划一下实现的步骤以及细节&#xff1a; 1、创建一个选手类&#x…...

【图片上传记录三】element-ui组件详解与封装(自定义上传、限制文件大小、格式以及图片尺寸)

业务上有需求是前端上传 jpg/png/gif 格式, 并且 尺寸为 150px * 150px,300px*300px,428*428px 的图片 同时在上传的同时需要携带用户的个人信息以及其他额外信息 因此在 element-upload 基础之上 实现这个需求需要在上传前检查图片的大小&#xff0c;格式以及尺寸如何上传也成…...

一个golang版本管理工具

GitHub - moqsien/gvc: GVC is a productive tool to manage your dev environment for multi platforms and machines. | GVC 是一个用于快速配置和管理多机器跨平台的开发环境的生产力工具。 目前&#xff0c;gvc拥有以下功能或特点&#xff1a; go编译器自动安装和添加环…...

SpringBoot整合Spring Security过滤器链加载执行流程源码分析

文章目录1.引言2.Spring Security过滤器链加载1.2.注册名为 springSecurityFilterChain的过滤器2、查看 DelegatingFilterProxy类3.查看 FilterChainProxy类3.1 查看 doFilterInternal方法。3.2 查看 getFilters方法。4 查看 SecurityFilterChain接口5 查看 SpringBootWebSecur…...

Jest使用

一、测试到底测什么 提到测试的时候&#xff0c;即使是最简单的一个代码块可能都让初学者不知所措。最常问的问题的是“我怎么知道要测试什么&#xff1f;”。如果你正在写一个 Web 应用&#xff0c;那么你每个页面每个页面的测试用户交互的方式&#xff0c;就是一个很好的开端…...

定位于企业数字化底座,开箱可用(spring cloud+Vue)基础框架,赶紧收藏!

项目介绍&#xff1a;JVS是什么&#xff1f;JVS是企业级应用构建的基础脚手架&#xff0c;提供开箱即用的基础功能集成&#xff0c;其中集成了账户管理、租户管理、用户权限体系、三方登录、环境配置、各种业务日志等功能&#xff0c;还提供了对接低代码、数据中台的能力。JVS能…...

java字符统计

问题描述 给定一个只包含大写字母的字符串 &#xfffd; S, 请你输出其中出现次数最多的字符。 如果有多个字母均出现了最多次, 按字母表顺序依次输出所有这些字母。 输入格式 一个只包含大写字母的字符串 &#xfffd; S. 输出格式 若干个大写字母&#xff0c;代表答案。 …...

C#:Krypton控件使用方法详解(第八讲) ——kryptonBreadCrumb

今天介绍的Krypton控件中的kryptonBreadCrumb&#xff0c;下面开始介绍这个控件的属性&#xff1a;首先要介绍的是RootItem属性和外观属性&#xff1a;RootItem属性组中包含属性如下&#xff1a;image属性&#xff1a;代表在文字对象的前方插入一个图片&#xff0c;属性值如下图…...

2023从0开始学性能(1) —— 性能测试基础【持续更新】

背景 不知道各位大佬有没遇到上面的情况&#xff0c;性能这个东西到底是什么&#xff0c;还是以前的358原则吗&#xff1f;明显并不是适用于现在了。多次想踏入性能测试门槛都以失败告终&#xff0c;这次就以系列的方式来督促自己真正踏进性能测试的门槛。 什么是性能测试 通…...

如何通过一台 iPhone 申请一个 icloud 邮箱账号 后缀为 @icloud.com

总目录 iOS开发笔记目录 从一无所知到入门 文章目录需求关键步骤步骤后续需求 在 iPhone 自带的邮箱软件中添加账号&#xff0c;排第一位的是 iCloud 邮箱&#xff1a; 选 iCloud 之后&#xff1a; 提示信息是exampleicloud.com&#xff0c;也就是说是有icloud.com为域的邮箱…...

SQL89 计算总和

描述OrderItems表代表订单信息&#xff0c;包括字段&#xff1a;订单号order_num和item_price商品售出价格、quantity商品数量。order_numitem_pricequantitya110105a211100a21200a421121a5510a2119a775【问题】编写 SQL 语句&#xff0c;根据订单号聚合&#xff0c;返回订单总…...

Netty高级应用之:编解码器与群聊天室开发

Netty高级应用之&#xff1a;编解码器与群聊天室开发 文章目录Netty高级应用之&#xff1a;编解码器与群聊天室开发Netty编解码器Java的编解码Netty编解码器概念解码器(Decoder)编码器(Encoder)编码解码器CodecNetty案例-群聊天室聊天室服务端编写聊天室客户端编写Netty编解码器…...

Vue的生命周期

Vue的生命周期是指Vue实例从创建到销毁的过程&#xff0c;它包括了以下几个阶段&#xff1a;初始化、编译、挂载、更新、渲染和销毁。 初始化&#xff1a;Vue实例创建时&#xff0c;会执行初始化过程&#xff0c;主要包括以下几个步骤&#xff1a; 初始化数据&#xff1a;Vue…...

MySQL —— 数据库基础

文章目录1. centos7 安装Mysql2. 数据库的概念3. 数据库下创建库&#xff0c;表4. 库&#xff0c;表 的本质5. 数据库服务器 和 库 &#xff0c;表的关系6. MySQL架构7. 存储引擎前言&#xff1a; 数据库是对数据进行管理的软件。1. centos7 安装Mysql 需要把系统自带的MySQL给…...

多线程知识点

多线程 基本知识 创建线程的常用三种方式&#xff1a; 继承Thread类实现Runnable接口实现Callable接口&#xff08;JDK1.5>&#xff09; public class ThreadTest extends Thread {Overridepublic void run() {System.out.println(this.getName() "..开始.."…...

有序表之红黑树

文章目录1、五个条件2、调整策略2.1 插入调整的情况2.1.1 情况一&#xff1a;插入节点是红色&#xff0c;其父节点也是红色2.1.2 情况二2.1.2 代码实现2.2 删除调整的情况2.2.1 情况一&#xff1a;双重黑节点的兄弟节点也是黑色&#xff0c;且其兄弟的两个孩子也是黑色2.2.2 情…...

HTTP状态码都有哪些?

100&#xff1a;客户必须继续发出请求 101&#xff1a;客户要求服务器根据请求转换HTTP协议版本 二&#xff1a;2xx 200&#xff1a;交易成功 201&#xff1a;提示知道新文件的URL 202&#xff1a;接受和处理、但处理未完成 203&#xff1a;返回信息不确定或不完整 204&#…...

Sketch+摹客,100M文件上传最快47s

哈喽&#xff0c;小摹来啦~ 去年12月底&#xff0c;摹客Sketch插件上新了「规范检查工具」&#xff0c;自功能上线以来&#xff0c;小摹收到了许多的好评及赞扬。 虽好评如潮&#xff0c;但我们不会止步不前&#xff0c;将持续攻克难点&#xff0c;旨在为大家提供更加稳定高效…...

关系型数据之分区分表分库

文章目录1.为什么需要分区分表分库2.各种分区分表分库的情况3.弊端3.1分区弊端3.2分表分库弊端1.为什么需要分区分表分库 数据量达到一定规模&#xff0c;进行常规的sql语句优化已经效果不大的情况下&#xff0c;常见为mysql数据库&#xff0c;单表的记录达到1000W和空间占用至…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

基于IDIG-GAN的小样本电机轴承故障诊断

目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) ​梯度归一化(Gradient Normalization)​​ (2) ​判别器梯度间隙正则化(Discriminator Gradient Gap Regularization)​​ (3) ​自注意力机制(Self-Attention)​​ 3. 完整损失函数 二…...

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving

地址&#xff1a;LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂&#xff0c;正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...

xmind转换为markdown

文章目录 解锁思维导图新姿势&#xff1a;将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件&#xff08;ZIP处理&#xff09;2.解析JSON数据结构3&#xff1a;递归转换树形结构4&#xff1a;Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...

CppCon 2015 学习:Time Programming Fundamentals

Civil Time 公历时间 特点&#xff1a; 共 6 个字段&#xff1a; Year&#xff08;年&#xff09;Month&#xff08;月&#xff09;Day&#xff08;日&#xff09;Hour&#xff08;小时&#xff09;Minute&#xff08;分钟&#xff09;Second&#xff08;秒&#xff09; 表示…...

GAN模式奔溃的探讨论文综述(一)

简介 简介:今天带来一篇关于GAN的,对于模式奔溃的一个探讨的一个问题,帮助大家更好的解决训练中遇到的一个难题。 论文题目:An in-depth review and analysis of mode collapse in GAN 期刊:Machine Learning 链接:...

el-amap-bezier-curve运用及线弧度设置

文章目录 简介示例线弧度属性主要弧度相关属性其他相关样式属性完整示例链接简介 ‌el-amap-bezier-curve 是 Vue-Amap 组件库中的一个组件,用于在 高德地图 上绘制贝塞尔曲线。‌ 基本用法属性path定义曲线的路径,可以是多个弧线段的组合。stroke-weight线条的宽度。stroke…...