更晚的晚风

导航

 

一、基础知识

1. 队列

队列的定义:

队列是在一端进行插入,再另一端进行删除的线性表。特点是先进先出(First In First Out),简称FIFO

获取队首数据:

对于一个队列来说,只能获取队首数据,一般不支持获取其他数据。

获取队列元素个数:

队列元素个数一般用一个额外变量存储,入队时加一,出队时减一。时间复杂度为O(1)

队列的判空:

当队列元素个数为零时,就是一个空队,空队不允许出队操作。

2. 双端队列

双端队列的定义:

双端队列是一种具有队列和栈的性质的数据结构,也就是deque(double-ended queue),是一种限定插入和删除操作在表的两端进行的线性表。这两端分别被称为队首和队尾.

模拟栈:

双端队列可以用来在一端进行插入和删除,从而实现栈的功能。比如在队首进行插入和删除操作,从而模拟栈的入栈和出栈的过程。

模拟队列:

双端队列也可以用于限定只在一端插入,另一端删除,从而实现队列的功能。比如在队尾进行插入,队首进行删除,从而模拟FIFO队列的入队和出队的过程。

清空队列:

队列的清空操作,就是一直出队,直到队列为空的过程,当队首和队尾正好错开一个位置时,就代表队尾为空了。

二、相关例题

  1. 最近的请求次数
  • 设计一个队列,队列中的元素保持单调递增
  • 每次调用ping操作时,将队首元素加上3000,如果比输入的t值小,则从队首弹出。
class RecentCounter {
    int front, rear;
    int data[10010];
public:
    RecentCounter() {
        front = 0;
        rear = -1;
    }
    
    int ping(int t) {
        data[++rear] = t;
        while(rear - front +1 > 0 && data[front] + 3000 < t) {
            front++;
        }
        return rear - front + 1;
    }
};

2.买票需要的时间

  • 定义一个结构体,存储位置和需要票的个数
  • 遍历一遍数组,将位置和需要票的个数在数组对象中进行存储
  • 再遍历一遍数组,把需要票的个数减1,时间就加1,模拟买票的过程。
  • 当需要的票的个数为0,也就是买到足够的票了之后,判断是不是在题目要求的位置。如果在对应的位置就返回需要的时间
  • 如果不是在对应的位置,就把这个数组对象在队尾进行入队操作,并且在队首进行出队操作,把旧的数组对象删掉。
  • 找不到就返回-1
class Solution {
    struct People {
        int pos;
        int val;
    };
    int front, rear;
    People data[20000];
public:
    int timeRequiredToBuy(vector<int>& tickets, int k) {
        //遍历一遍
        int i;
        int time = 0;
        front = rear = 0;
        for(i = 0; i < tickets.size(); ++i) {
            People a;
            a.pos = i;
            a.val = tickets[i];
            data[rear++] = a;
        }
        while(front < rear) {
            //遍历一遍,做判断
            //头插法
            People p = data[front++];//指向队列中新的元素,并删除旧的元素
            time++;
            --p.val;
            if(p.val == 0) {
                if(p.pos == k) {
                    return time;
                }
            } else {
                //p不为0时,添加到队列的末端
                data[rear++] = p;
            }
        }
        //找不到,返回-1
        return -1;
    }
};
  1. 设计循环双端队列
  • 这道题不用设计双端队列
  • 因为执行次数不会超过2000次,所以可以初始化一个元素个数为4000的数组,并把队首和队尾初始化在数组中间。
  • 比如front=2000, rear = front-1;而rear-front+1就是队列中的元素个数
  • 要注意什么时候时++front,还是front++;反之,rear也一样。
class MyCircularDeque {
    #define CENTER 2500
    int data[5000];
    int cap;
    int front, rear;
public:
    MyCircularDeque(int k) {
        cap = k;
        front = CENTER;
        rear = front - 1;
    }

    int getCount() {
        return rear - front + 1;
    }

    bool insertFront(int value) {
        if(isFull()) return false;
        data[--front] = value;
        return true;

    }
    
    bool insertLast(int value) {
        if(isFull()) return false;
        data[++rear] = value;
        return true;

    }
    
    bool deleteFront() {
        if(isEmpty()) return false;
        front++;
        return true;

    }
    
    bool deleteLast() {
        if(isEmpty()) return false;
        rear--;
        return true;

    }
    
    int getFront() {
        if(isEmpty()) return -1;
        return data[front];

    }
    
    int getRear() {
        if(isEmpty()) return -1;
        return data[rear];

    }
    
    bool isEmpty() {
        return getCount() == 0;

    }
    
    bool isFull() {
        return getCount() == cap;

    }
};

/**
 * Your MyCircularDeque object will be instantiated and called as such:
 * MyCircularDeque* obj = new MyCircularDeque(k);
 * bool param_1 = obj->insertFront(value);
 * bool param_2 = obj->insertLast(value);
 * bool param_3 = obj->deleteFront();
 * bool param_4 = obj->deleteLast();
 * int param_5 = obj->getFront();
 * int param_6 = obj->getRear();
 * bool param_7 = obj->isEmpty();
 * bool param_8 = obj->isFull();
 */
  1. 设计前中后队列
  • 跟上一道题目类似,数据量小,所以可以用数组
  • 在中间插入和中间删除的时候,可以用数学归纳法进行分析
class FrontMiddleBackQueue {
    #define CENTER 2500
    int data[5000];
    int front, rear;
public:
    FrontMiddleBackQueue() {
        front = CENTER;
        rear = front - 1;

    }
    
    void pushFront(int val) {
        data[--front] = val;

    }
    
    void pushMiddle(int val) {
        int x = rear - front + 1;//元素个数
        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(rear - front + 1 == 0) return -1;
        return data[front++];

    }
    
    int popMiddle() {
        int x = rear - front + 1;
        if(x == 0) return -1;
        int y = front+(x-1) / 2;
        int temp = data[y];
        for(int i = y; i < rear; ++i) {
            data[i] = data[i+1];
        }
        rear--;
        return temp;

    }
    
    int popBack() {
        if(rear - front + 1 == 0) return -1;
        return data[rear--];

    }
};

/**
  ****** ***** 2
  ***** **** 2
  **** *** 1
  *** ** 1
  ** * 0
 * Your FrontMiddleBackQueue object will be instantiated and called as such:
 * FrontMiddleBackQueue* obj = new FrontMiddleBackQueue();
 * obj->pushFront(val);
 * obj->pushMiddle(val);
 * obj->pushBack(val);
 * int param_4 = obj->popFront();
 * int param_5 = obj->popMiddle();
 * int param_6 = obj->popBack();
 */
posted on 2022-05-16 16:04  更晚的晚风  阅读(131)  评论(0)    收藏  举报