循环队列

首页 / 💻 代码 / 正文

Leetcode.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 的范围内;
请不要使用内置的队列库。


动图演示

06fbb04d14c3455ba21e4a177352ba20.gif
(动图来自 https://blog.csdn.net/qq_42996461/article/details/125674149)

实现容量为k的循环队列,可以定义一个大小为k的动态数组。同时,也可以通过链表实现。本文通过动态数组vector实现,并且在此过程中不使用指针。变量的定义与解释如下:

private:
    vector<int> queue; //循环队列
    int front; //队首
    int rear; //队尾
    int capacity; //队列的容量

对于使用指针实现容量为k的循环队列,需要定义(k+1)的空间,用于判空判满。但如果不使用指针实现,只需定义k的空间即可。


验空与验满

由于存入队列的值的大小在0-1000之间,因此我们可以在队列初始化以及删除元素时用-1来代替数组中元素为空的情况。所以在验空和验满时,只需要判断数组中是否 全部元素为-1 或者 全部元素不为-1 来实现。

    bool isEmpty() {
        
        for (int cptime = 0; cptime < capacity; cptime++)
        {
            if (queue[cptime] != -1)
            {
                return false;
                
            }
        }
        return true;
    }

    bool isFull() {
        
        for (int cptime = 0; cptime < capacity; cptime++)
        {
            if (queue[cptime] == -1)
            {
                
                return false;
               
            }
        }
        return true;

    }

入队

在进行入队操作时,首先应该考虑队列是否已满,如果队列已满,那就无法进行入队操作,直接返回false即可。再考虑队尾是否处在数组的末尾,如果是这样的话,那就应该在数组的头部插入数据,并且将队尾指向数组的头部。如果不在数组的末尾,那么按顺序插入即可。

    bool enQueue(int value) {
        if (isFull() == true)
        {
            return false;
        } 
        if (rear == capacity - 1)
        {
            rear = 0;
            queue[rear] = value;
            return true;
        }
        queue[rear+1] = value;
        rear++;
        return true;
    }

由于队列遵循先入先出原则。因此在“再考虑队尾是否处在数组的末尾,如果是这样的话,那就应该在数组的头部插入数据,并且将队尾指向数组的头部。”的时候时,不需要考虑数组的头部是否有数据。因为对于队列来说,只会出现一种“队首和队尾都有数据,而队尾的指针指向数组的末尾”的情况,那就是队列已满。但由于在入队时我们优先考虑了队列是否已满的情况,所以在此步就不需要考虑了。


出队

出队的逻辑与入队相似。

    bool deQueue() {
        if (isEmpty() == true)
        {
            return false;
        }
        if (front == capacity - 1)
        {
            front = 0;
            queue[capacity-1] = -1;
            return true;
        }
        queue[front] = -1;
        front++;
        return true;
    }

取出队首/队尾数据

只需要获取front或者rear指向的数据即可。(由于题目没有要求,因此本代码未考虑当队列为空的情况)

    int Front() {
        if (isEmpty() == true)
        {
            return -1;
        }
        return queue[front];

    }

    int Rear() {
        if (isEmpty() == true)
        {
            return -1;
        }
        return queue[rear];
    }

最终代码

class MyCircularQueue {
private:
    vector<int> queue;
    int front;
    int rear;
    int capacity;
public:
    MyCircularQueue(int k) {
        queue.resize(k);
        front = 0;
        rear = -1;
        capacity = k;
        for (int cptime = 0; cptime < k; cptime++)
        {
            queue[cptime] = -1;
        }
    }

    bool enQueue(int value) {
        if (isFull() == true)
        {
            return false;
        } 
        if (rear == capacity - 1)
        {
            rear = 0;
            queue[rear] = value;
            return true;
        }
        queue[rear+1] = value;
        rear++;
        return true;
    }

    bool deQueue() {
        if (isEmpty() == true)
        {
            return false;
        }
        if (front == capacity - 1)
        {
            front = 0;
            queue[capacity-1] = -1;
            return true;
        }
        queue[front] = -1;
        front++;
        return true;
    }

    int Front() {
        if (isEmpty() == true)
        {
            return -1;
        }
        return queue[front];

    }

    int Rear() {
        if (isEmpty() == true)
        {
            return -1;
        }
        return queue[rear];
    }

    bool isEmpty() {
        bool sum = true;
        for (int cptime = 0; cptime < capacity; cptime++)
        {
            if (queue[cptime] != -1)
            {
                sum = false;
                break;
            }
        }
        return sum;
    }

    bool isFull() {
        bool sum = true;
        for (int cptime = 0; cptime < capacity; cptime++)
        {
            if (queue[cptime] == -1)
            {
                sum = false;
                break;
            }
        }
        return sum;

    }
};

动态拓展队列空间与链表实现

详情见2019计算机考研408数据结构大题。

【2019统考真题】请设计一个队列,要求满足:①初始时队列为空;②入队时,允许增加队列占用空间;③出队后,出队元素所占用的空间可重复使用,即整个队列所占用的空间只增不减;④入队操作和出队操作的时间复杂度始终保持为O ( 1 ) O(1)O(1)。请回答下列问题:
1)该队列是应选择链式存储结构,还是应选择顺序存储结构?
2)画出队列的初始状态,并给出判断队空和队满的条件。
3 ) 画出第一个元素入队后的队列状态。
4)给出入队操作和出队操作的基本过程。

如果使用动态数组实现的话,可以使用 resize 方法拓展空间。但对于本题要求的链表实现,可以见 https://blog.csdn.net/qq_50710984/article/details/113946205


打赏qwq
文章目录