前言
这是五月集训的第十六日,今日的训练内容是 队列
队列的笔记
今日学习的内容是队列,队列其实就是一种操作受限的线性表,只允许在一头写入数据在另一头删除数据,在《数据结构》中也学习过相关的内容,但实际上去上机写代码还没有过,因此今日将记录一下链式存储结构的队列的构建、插入结点、删除结点等操作的代码,并把个人的理解等记录在注释中,也方便日后的复习(也方便写题的时候当c++的对象用(x))
typedef struct{ //定义结点
int data;
struct Node* next;
}Node;
typedef struct{ //定义队列
Node *head,*tail;
int size; //队列长度
}que;
void que_enter(que *a,int dt){ //用尾插法插入队列
Node *b = (Node*)malloc(sizeof(Node)); //申请内存
b->data = dt;
b->next = NULL;
if(a->tail){ //如果尾结点不为空
a->tail->next = b; //尾结点的next指针指向新插入的结点
a->tail = b; //尾结点变为插入的结点
}else{ //如果尾结点为空
a->head = a->tail = b; //头结点和尾结点都是这个
}
a->size++; //插入完结点后队列长度增加
}
void que_out(que *a){ //将头结点移出队列
Node *tmp = a->head; //定义一个中继结点,复制头结点,防止链表断开
a->head = tmp->next; //使头结点变为tmp所指的下一个结点,原结点解链
free(tmp); //释放tmp结点
a->size--; //队列长度减一
if(a->size==0) a->tail = NULL; //长度为0时使尾结点也一起指向空
}
void que_destroy(que *a){ //清空队列
while(a->size!=0){ //队列长度不为0时就出队
que_out(a);
}
}
解题报告
1.力扣933
原题链接
题目概述
写一个 RecentCounter 类来计算特定时间范围内最近的请求。
请你实现 RecentCounter 类:
RecentCounter() 初始化计数器,请求数为 0 。
int ping(int t) 在时间 t 添加一个新请求,其中 t 表示以毫秒为单位的某个时间,并返回过去 3000 毫秒内发生的所有请求数(包括新请求)。确切地说,返回在 [t-3000, t] 内发生的请求数。
保证 每次对 ping 的调用都使用比之前更大的 t 值。
解题思路
这题比较简单,简单的构建一个队列,然后往内部填充元素的时候一边填入就一边移动尾指针,并且使得计数器增加,然后实时判断 t 减去头指针指向的元素的大小是不是大于3000,如果大于3000的话就移动头指针,并且使得计数器减一,直到小于3000的时候就跳出循环,返回计数器。
源码剖析
typedef struct {
int front; //头指针
int rear; //尾指针
int *que;
int cal;
} RecentCounter;
RecentCounter* recentCounterCreate() {
RecentCounter* obj =(RecentCounter*)malloc(sizeof(RecentCounter));
obj->front=0;
obj->rear=0;
obj->cal=0;
obj->que=(int*)malloc(sizeof(int)*10000); //至多只需要调用10e4次
return obj;
}
int recentCounterPing(RecentCounter* obj, int t) {
obj->que[obj->rear++] = t; //首先将请求添加到队尾,并移动尾指针
obj->cal++;
if(t-obj->que[obj->front]>3000){ //当t减去头指针指向元素大于3000就向后移动头指针
while(1){
obj->front++;
obj->cal--;
if(t-obj->que[obj->front]<=3000) break;
}
}
return obj->cal;
}
void recentCounterFree(RecentCounter* obj) {
free(obj->que);
free(obj);
}
/**
* Your RecentCounter struct will be instantiated and called as such:
* RecentCounter* obj = recentCounterCreate();
* int param_1 = recentCounterPing(obj, t);
* recentCounterFree(obj);
*/
2.力扣2073
原题链接
题目概述
有 n 个人前来排队买票,其中第 0 人站在队伍 最前方 ,第 (n - 1) 人站在队伍 最后方 。
给你一个下标从 0 开始的整数数组 tickets ,数组长度为 n ,其中第 i 人想要购买的票数为 tickets[i] 。
每个人买票都需要用掉 恰好 1 秒 。一个人 一次只能买一张票 ,如果需要购买更多票,他必须走到 队尾 重新排队(瞬间 发生,不计时间)。如果一个人没有剩下需要买的票,那他将会 离开 队伍。
返回位于位置 k(下标从 0 开始)的人完成买票需要的时间(以秒为单位)。
解题思路
首先把数组填入一个队列之中,在填入的过程中,使用一个指针指向第 k 个结点的数据域,一旦当这个数据域变为0了说明循环已经结束了,具体的分析可以见源码剖析的注释部分,写的很详细。
源码剖析
typedef struct{ //定义结点
int data;
struct Node* next;
}Node;
typedef struct{ //定义队列
Node *head,*tail;
int size;
}que;
void que_enter(que *a,int dt){ //用尾插法插入队列
Node *b = (Node*)malloc(sizeof(Node)); //申请内存
b->data = dt;
b->next = NULL;
if(a->tail){ //如果尾结点不为空
a->tail->next = b; //尾结点的next指针指向新插入的结点
a->tail = b; //尾结点变为插入的结点
}else{ //如果尾结点为空
a->head = a->tail = b; //头结点和尾结点都是这个
}
a->size++; //插入完结点后队列长度增加
}
void que_out(que *a){ //将头结点移出队列
Node *tmp = a->head; //定义一个中继结点,复制头结点,防止链表断开
a->head = tmp->next; //使头结点变为tmp所指的下一个结点,原结点解链
free(tmp); //释放tmp结点
a->size--; //队列长度减一
if(a->size==0) a->tail = NULL; //长度为0时使尾结点也一起指向空
}
int timeRequiredToBuy(int* tickets, int ticketsSize, int k){
que *a;
que_start(a);
int *aim;
int ans = 0;
for(int i = 0;i<ticketsSize;++i){ //将数组填入队列中
que_enter(a,tickets[i]);
if(i==k-1) aim=&a->tail->data; //使用一个指针标记k位所在结点的数据
}
while(1){ //当标记项不等于 0 时
a->head->data--;
if(*aim==0){ //如果是标记项直接结束循环防止bug
ans++;
break;
}
if(a->head->data==0){
que_out(a); //不然就出队
}
else{ //否则把头结点放入尾巴
que_enter(a,a->head->data);
que_out(a);
}
ans++;
}
return ans;
}
注意 这种做法仍然还有没解决的bug:Line 13: Char 9: runtime error: member access within null pointer of type ‘struct que’ [solution.c],也就是插入队列操作的第四行。
3.力扣641
原题链接
题目概述
设计实现双端队列。
实现 MyCircularDeque 类:
MyCircularDeque(int k) :构造函数,双端队列最大为 k 。
boolean insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true ,否则返回 false 。
boolean insertLast() :将一个元素添加到双端队列尾部。如果操作成功返回 true ,否则返回 false 。
boolean deleteFront() :从双端队列头部删除一个元素。 如果操作成功返回 true ,否则返回 false 。
boolean deleteLast() :从双端队列尾部删除一个元素。如果操作成功返回 true ,否则返回 false 。
int getFront() ):从双端队列头部获得一个元素。如果双端队列为空,返回 -1 。
int getRear() :获得双端队列的最后一个元素。 如果双端队列为空,返回 -1 。
boolean isEmpty() :若双端队列为空,则返回 true ,否则返回 false 。
boolean isFull() :若双端队列满了,则返回 true ,否则返回 false 。
解题思路
简单的构筑一下双端队列就可以了。
源码剖析
typedef struct {
int* base;//数组基地址
int front;//队头
int rear;//队尾
int size;//队列当前元素长度
int maxlength;//队列的最大长度
} MyCircularDeque;
MyCircularDeque* myCircularDequeCreate(int k) {
MyCircularDeque* node=(MyCircularDeque*)malloc(sizeof(MyCircularDeque));
node->base=(int*)malloc(sizeof(int)*k);
node->front=-1;
node->rear=-1;
node->size=0;
node->maxlength=k;
return node;
}
bool myCircularDequeIsEmpty(MyCircularDeque* obj) {
if(obj->size==0)
return true;
else
return false;
}
bool myCircularDequeIsFull(MyCircularDeque* obj) {
if(obj->size>=obj->maxlength)
return true;
else
return false;
}
bool myCircularDequeInsertFront(MyCircularDeque* obj, int value) {
if(myCircularDequeIsFull(obj))
return false;
if(myCircularDequeIsEmpty(obj)){
obj->front=(obj->front+1)%obj->maxlength;
obj->rear=(obj->rear+1)%obj->maxlength;
obj->base[obj->front]=value;
obj->size++;
return true;
//队列为空时,队头插入元素,队头队尾指针都要指向它
}
obj->front=(obj->front-1+obj->maxlength)%obj->maxlength;
obj->base[obj->front]=value;
obj->size++;
return true;
}
bool myCircularDequeInsertLast(MyCircularDeque* obj, int value) {
if(myCircularDequeIsFull(obj))
return false;
if(myCircularDequeIsEmpty(obj)){
obj->front=(obj->front+1)%obj->maxlength;
obj->rear=(obj->rear+1)%obj->maxlength;
obj->base[obj->rear]=value;
obj->size++;
return true;
//队列为空时,队尾插入元素,队头队尾指针都要指向它
}
obj->rear=(obj->rear+1)%obj->maxlength;
obj->base[obj->rear]=value;
obj->size++;
return true;
}
bool myCircularDequeDeleteFront(MyCircularDeque* obj) {
if(myCircularDequeIsEmpty(obj))
return false;
if(obj->front==obj->rear){
obj->front=-1;
obj->rear=-1;
obj->size--;
return true;
//判断队列是不是只有一个元素,若是,删除之后,队头队尾指针重置为-1
}
obj->front=(obj->front+1)%obj->maxlength;
obj->size--;
return true;
}
bool myCircularDequeDeleteLast(MyCircularDeque* obj) {
if(myCircularDequeIsEmpty(obj))
return false;
if(obj->front==obj->rear){
obj->front=-1;
obj->rear=-1;
obj->size--;
return true;
}
//判断队列是不是只有一个元素,若是,删除之后,队头队尾指针重置为-1
obj->rear=(obj->rear-1+obj->maxlength)%obj->maxlength;
obj->size--;
return true;
}
int myCircularDequeGetFront(MyCircularDeque* obj) {
if(myCircularDequeIsEmpty(obj))
return -1;
return obj->base[obj->front];
}
int myCircularDequeGetRear(MyCircularDeque* obj) {
if(myCircularDequeIsEmpty(obj))
return -1;
return obj->base[obj->rear];
}
void myCircularDequeFree(MyCircularDeque* obj) {
free(obj->base);
obj->size=0;
obj->maxlength=0;
}
/**
* Your MyCircularDeque struct will be instantiated and called as such:
* MyCircularDeque* obj = myCircularDequeCreate(k);
* bool param_1 = myCircularDequeInsertFront(obj, value);
* bool param_2 = myCircularDequeInsertLast(obj, value);
* bool param_3 = myCircularDequeDeleteFront(obj);
* bool param_4 = myCircularDequeDeleteLast(obj);
* int param_5 = myCircularDequeGetFront(obj);
* int param_6 = myCircularDequeGetRear(obj);
* bool param_7 = myCircularDequeIsEmpty(obj);
* bool param_8 = myCircularDequeIsFull(obj);
* myCircularDequeFree(obj);
*/
4.力扣1670
原题链接
题目概述
请你设计一个队列,支持在前,中,后三个位置的 push 和 pop 操作。
请你完成 FrontMiddleBack 类:
FrontMiddleBack() 初始化队列。
void pushFront(int val) 将 val 添加到队列的 最前面 。
void pushMiddle(int val) 将 val 添加到队列的 正中间 。
void pushBack(int val) 将 val 添加到队里的 最后面 。
int popFront() 将 最前面 的元素从队列中删除并返回值,如果删除之前队列为空,那么返回 -1 。
int popMiddle() 将 正中间 的元素从队列中删除并返回值,如果删除之前队列为空,那么返回 -1 。
int popBack() 将 最后面 的元素从队列中删除并返回值,如果删除之前队列为空,那么返回 -1 。
请注意当有 两个 中间位置的时候,选择靠前面的位置进行操作。比方说:将 6 添加到 [1, 2, 3, 4, 5] 的中间位置,结果数组为 [1, 2, 6, 3, 4, 5] 。
从 [1, 2, 3, 4, 5, 6] 的中间位置弹出元素,返回 3 ,数组变为 [1, 2, 4, 5, 6] 。
解题思路
本题比较复杂,暂做记录,希望下个月的这时候可以做的出来。
源码剖析
1019



被折叠的 条评论
为什么被折叠?



