[LeetCode] Design Circular Queue 設計環形隊列


 

Design your implementation of the circular queue. The circular queue is a linear data structure in which the operations are performed based on FIFO (First In First Out) principle and the last position is connected back to the first position to make a circle. It is also called "Ring Buffer".

One of the benefits of the circular queue is that we can make use of the spaces in front of the queue. In a normal queue, once the queue becomes full, we cannot insert the next element even if there is a space in front of the queue. But using the circular queue, we can use the space to store new values.

Your implementation should support following operations:

  • MyCircularQueue(k): Constructor, set the size of the queue to be k.
  • Front: Get the front item from the queue. If the queue is empty, return -1.
  • Rear: Get the last item from the queue. If the queue is empty, return -1.
  • enQueue(value): Insert an element into the circular queue. Return true if the operation is successful.
  • deQueue(): Delete an element from the circular queue. Return true if the operation is successful.
  • isEmpty(): Checks whether the circular queue is empty or not.
  • isFull(): Checks whether the circular queue is full or not.

 

Example:

MyCircularQueue circularQueue = new MyCircularQueue(3); // set the size to be 3
circularQueue.enQueue(1);  // return true
circularQueue.enQueue(2);  // return true
circularQueue.enQueue(3);  // return true
circularQueue.enQueue(4);  // return false, the queue is full
circularQueue.Rear();  // return 3
circularQueue.isFull();  // return true
circularQueue.deQueue();  // return true
circularQueue.enQueue(4);  // return true
circularQueue.Rear();  // return 4

 

Note:

  • All values will be in the range of [0, 1000].
  • The number of operations will be in the range of [1, 1000].
  • Please do not use the built-in Queue library.

 

這道題讓我們設計一個環形的隊列,說是不能使用內置的 queue 類,並且讓我們實現一系列的成員函數,如進隊,出隊,取首尾元素,以及判空,判滿等等。那么博主最先想到的就是用一個數組 data 來實現,並且用一個變量 size 來保存我們的環形隊列的大小。先來實現最簡單的判空和判滿函數吧,判空就是判斷 data 數組是否為空,判滿就是看 data 數組的大小是否等於 size。然后是取首尾元素,需要先對數組判空,然后取首尾元素即可。進隊列函數先要判滿,然后加入 data 數組,出隊列函數,先要判空,然后去掉數組的首元素即可,參見代碼如下:

 

解法一:

class MyCircularQueue {
public:
    MyCircularQueue(int k) {
        size = k;
    }
    bool enQueue(int value) {
        if (isFull()) return false;
        data.push_back(value);
        return true;
    }
    bool deQueue() {
        if (isEmpty()) return false;
        data.erase(data.begin());
        return true;
    }
    int Front() {
        if (isEmpty()) return -1;
        return data.front();
    }
    int Rear() {
        if (isEmpty()) return -1;
        return data.back();
    }
    bool isEmpty() {
        return data.empty();
    }
    bool isFull() {
        return data.size() >= size;
    }
    
private:
    vector<int> data;
    int size;
};

 

做完上面的方法有沒有一種感覺,這跟環形 Circular 有毛線關系,還有題目要求中的第二段話里的“我們可以使用隊列前面的空間”,完全沒有用到啊。其實上面的解法並不是本題真正想要考察的內容,我們要用上環形 Circular 的性質,之前我們貌似應該做過環形數組的題目吧,提到環形數組,博主最先想到的就是坐標加1,再對數組長度取余。這是數組能夠環形的關鍵,那么這里也一樣,我們除了使用 size 來記錄環形隊列的最大長度之外,還要使用三個變量,head,tail,cnt,分別來記錄隊首位置,隊尾位置,和當前隊列中數字的個數,這里我們將head初始化為 0,tail初始化為 k-1,head 是指向數組范圍內的起始位置,tail 指向數組范圍內的結束位置。那么在 Front() 函數,由於我們要返回起始位置的數字,為了不越界,進行環形走位,還要對 size 取余,於是就變成了 head % size,同理,對於 Rear() 函數,我們要返回結束位置的數字,為了不越界,並且環形走位,tail 要先加上size,再對 size 取余,於是就變成了 (tail+size) % size。

還是從簡單的做起,判空就看當前個數 cnt 是否為0,判滿就看當前個數 cnt 是否等於 size。接下來取首尾元素,先進行判空,然后根據 head 和tail 取即可,記得使用上循環數組的性質,要對 size 取余。再來看進隊列函數,先進行判滿,tail 要移動到下一位,為了避免越界,我們使用環形數組的經典操作,加1之后對長度取余,然后將新的數字加到當前的tail位置,cnt 再自增1即可。同樣,出隊列函數先進行判空,隊首位置 head 要向后移動一位,同樣進行加1之后對長度取余的操作,到這里就可以了,不用真正的去刪除數字,因為 head 和 tail 限定了我們的當前隊列的范圍,然后 cnt 自減1,參見代碼如下:

 

解法二:

class MyCircularQueue {
public:
    MyCircularQueue(int k) {
        size = k; head = k - 1; tail = 0; cnt = 0;
        data.resize(k);
    }
    bool enQueue(int value) {
        if (isFull()) return false;
        data[tail] = value;
        tail = (tail + 1) % size;
        ++cnt;
        return true;
    }
    bool deQueue() {
        if (isEmpty()) return false;
        head = (head + 1) % size;
        --cnt;
        return true;
    }
    int Front() {
        return isEmpty() ? -1 : data[(head + 1) % size];
    }
    int Rear() {
        return isEmpty() ? -1 : data[(tail - 1 + size) % size];
    }
    bool isEmpty() {
        return cnt == 0;
    }
    bool isFull() {
        return cnt == size;
    }
    
private:
    vector<int> data;
    int size, cnt, head, tail;
};

 

論壇上還見到了使用鏈表來做的解法,由於博主比較抵觸在解法中新建class,所以這里就不貼了,可以參見這個帖子

 

類似題目:

Design Circular Deque

 

參考資料:

https://leetcode.com/problems/design-circular-queue/

https://leetcode.com/problems/design-circular-queue/discuss/149420/Concise-Java-using-array

https://leetcode.com/problems/design-circular-queue/discuss/162759/JAVA-Pass-All-Test-Cases-100-O(1)

 

LeetCode All in One 題目講解匯總(持續更新中...) 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM