《五月集训》(第十六天)——队列

前言

        欢迎大家积极在评论区留言发表自己的看法,知无不言,言无不尽,养成每天刷题的习惯,也可以自己发布优质的解题报告,供社区一同鉴赏,吸引一波自己的核心粉丝。
        今天是五月集训第十六天:队列🔥🔥

一、练习题目

        933. 最近的请求次数
        2073. 买票需要的时间
        641. 设计循环双端队列
        1670.设计前中后队列

二、算法思路

  • 1、933. 最近的请求次数:队列简单模拟。
  • 2、2073. 买票需要的时间:设计一个结构体用来记录买票人在数组中的位置和需要买票的总数,用这个结构体定义一个数组模拟队列。
  • 3、641. 设计循环双端队列:其实不需要用双端队列用数组来模拟就可以,或者链表,链表便于插入和删除,数组利于查找,各有好处,那我选择数组吧😄。
  • 4、1670.设计前中后队列:上一题的应用题,做起来非常爽~✈️

三、源码剖析

// 933. 最近的请求次数
class RecentCounter {
    queue<int> q;
public:
    RecentCounter() {

    }
    
    int ping(int t) {
        q.push(t); 
        while(q.front() < t - 3000) {
            q.pop(); //(1)
        }
        return q.size();
    }
};
  • 1、队首大于3000弹出队列。
// 2073. 买票需要的时间
class Solution {
    struct People
    {
        int pos, ticket;
    }; //(1)
    int front, rear;
    People data[20000]; //(2)
public:
    int timeRequiredToBuy(vector<int>& tickets, int k) {
        front = rear = 0;
        int time = 0;
        for(int i = 0; i < tickets.size(); ++i) {
            People a;
            a.pos = i;
            a.ticket = tickets[i];
            data[rear++] = a;
        } //(3)
        while (front < rear) //(4)
        {
            People p = data[front++];
            time++;
            --p.ticket;
            if(p.ticket == 0) {
                if(p.pos == k) {
                    return time;
                }
            } else {
                data[rear++] = p;
            }
        }
        return -1;
    }
};
  • 1、设计一个结构体;
  • 2、定义一个结构体数组来模拟队列;
  • 3、将题目中的tickets转化到结构数组队列中;
  • 4、当队列不为空时候继续遍历,进行循环一次相当于模拟购票一次,时间加一,如果当前这个人票买完了,同时是我们要找的那个人,返回购票的总时间否则放到队尾继续模拟。
// 641. 设计循环双端队列
class MyCircularDeque {
    #define CENTER 2000
    int data[4000]; //(1)
    int front, rear;
    int cap;
public:
    MyCircularDeque(int k) {
        cap = k;
        front = CENTER;
        rear = front - 1; //(2)
    }
    
    bool insertFront(int value) {
        if(isFull()) {
            return false;
        } else {
            data[--front] = value;
        }
        return true;
    }
    
    bool insertLast(int value) {
        if(isFull()) {
            return false;
        } else {
            data[++rear] = value;
        }
        return true;
    }
    
    bool deleteFront() {
        if(isEmpty()) {
            return false;
        } else {
            ++front;
        }
        return true;
    }
    
    bool deleteLast() {
        if(isEmpty()) {
            return false;
        } else {
            --rear;
        }
        return true;
    }
    
    int getFront() {
        if(isEmpty()) {
            return -1;
        }
        return data[front];
    }
    
    int getRear() {
        if(isEmpty()) {
            return -1;
        }
        return data[rear];
    }
    
    bool isEmpty() {
        return (rear - front + 1) == 0 ? 1 : 0;
    }
    
    bool isFull() {
        return (rear - front + 1) == cap ? 1 : 0;
    }
};
  • 1、因为执行的次数不会超过2000,所以设置一个长度为4000的数组,并且将队首队尾初始化为数组的中间,这样做的话防止要去考虑数组下标越界的问题;
  • 2、初始化front = 2000, rear = front - 1,这样做的好处是 rear - front + 1可以判断队列的长度,是不是空或者满。
// 1670. 设计前中后队列
class FrontMiddleBackQueue {
    #define CENTER 2000
    int data[4000];
    int rear, front; //(1)
public:
    int getCount() {
        return rear - front + 1;
    }
    FrontMiddleBackQueue() {
        front = CENTER;
        rear = CENTER - 1;
    }
    
    void pushFront(int val) {
        data[--front] = val;
    }
    
    //  X           front - 1
    //  XX          front + 1
    //  xxx         front + 1
    //  xxxx        front + 2
    //  xxxxx       front + 3
    void pushMiddle(int val) {
        if(getCount() == 1) {
            pushFront(val);
            return;
        }
        int x = getCount();
        int y = front + x / 2;
        for(int i = rear + 1; i > y; --i) {
            data[i] = data[i - 1];
        }
        data[y] = val;
        ++rear;
    }
    
    void pushBack(int val) {
        data[++rear] = val;
    }
    
    int popFront() {
        if(getCount() != 0) {
            return data[front++];
        }
        return -1;
    }
    
    // x            front
    // xx           front
    // xxx          front + 1
    // xxxx         front + 1
    // xxxxx        front + 2
    int popMiddle() {
        if(getCount() != 0) {
            int x = getCount();
            int y = front + (x-1) / 2;
            int tmp = data[y];
            for(int i = y; i < rear; ++i) {
                data[i] = data[i + 1];
            }
            rear--;
            return tmp;
        }
        return -1;
    }
    
    int popBack() {
        if(getCount() != 0) {
            return data[rear--];
        }
        return -1;
    }
};
  • 1、思想和上一题一样我就不赘述;
  • 2、在中间插入的时候需要注意的,插入的位置通过数学归纳法得出,大佬的思想😄
      1. 插入中间的操作:如果是只用一个就插最前面正常操作,所以这个单独判断,队列长度大于1的 插 入 位 置 = f r o n t + 队 列 长 度 / 2 插入位置 = front + 队列长度 / 2 =front+/2
      2. 删除操作,归纳出来是 插 入 位 置 = f r o n t + ( 队 列 长 度 − 1 ) / 2 插入位置 = front + (队列长度 - 1) / 2 =front+(1)/2
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值