大家都知道佇列是可以用陣列來模擬的,可以先開闢一段定長的陣列空間,然後分別使用兩個變數head和tail來代指佇列的頭和尾,從而維護整個佇列,相信到這裡大家都比較熟悉。不過這種做法是有弊端的,比如說下圖這種情況
假設經過不斷地增刪元素,Head和Tail已經來到了陣列最後兩個位置,這時候整個佇列中只有兩個元素,並且我們也不能再增加元素了,因為已經到達了容量的上限。然而,這時候前面一大片連續空間就造成了浪費。因此我們重新設想一下
這是另外一種構思,此時佇列當中存有三個元素,那麼該怎麼實現呢?
#define MAXSIZE (1 << 16)
template <typename _Tp>
struct fifo {
uint16_t front = 1;
uint16_t end = 1;
int frontCount = -0x3f3f3f3f;
int endCount = -0x3f3f3f3f;
_Tp arr[MAXSIZE];
}
對於front和end兩個變數,可以用無符號整型來實現,當無符號整型溢位的時候,會自然的變成0,問題就爽快的解決了。不過這裡還引入了兩個變數,frontCount和endCount,這是為了判斷front是在end的前面還是後面。換句話說,當end發生溢位的時候,end就來到了front前面,這時候endCount就加1,frontCount同理。
bool empty() {
if (endCount > frontCount)
return false;
return (front == end) ? true : false;
}
bool full() {
if (endCount > frontCount && end == front)
return true;
return false;
}
std::size_t size() {
if (full())
return MAXSIZE;
else if (empty())
return 0;
else if (frontCount == endCount)
return (end - front);
else
return (MAXSIZE - front + end);
}
以上是判斷佇列容量的一些相關成員函式,實現都比較簡略,較為關鍵的是入隊和出隊的操作實現。
void push(_Tp&& element) {
if (full()) {
std::cerr << "Full\n";
return;
}
if (((uint16_t) (end + 1)) == 0)
++endCount;
arr[++end] = element;
}
void push(const _Tp& element) {
if (full()) {
std::cerr << "Full\n";
return;
}
if (((uint16_t) (end + 1)) == 0)
++endCount;
arr[++end] = element;
}
std::optional<_Tp> pop() {
if (empty())
return std::nullopt;
if (((uint16_t) (front + 1)) == 0)
++frontCount;
return arr[++front];
}
有個小區別,這裡的front指向的位置在邏輯上是不儲存元素的,其它的和開篇所講的都差不多。那麼,對於(end + 1) ,為什麼要加一個強制轉換呢?因為如果不加的話,end和1進行運算之後就提升為了int型別,這時候結果是int型別的,它不會因為溢位而變成0。
完整程式碼:
#include <iostream>
#include <cstdint>
#include <optional>
#define MAXSIZE (1 << 16)
template <typename _Tp>
struct fifo {
uint16_t front = 1;
uint16_t end = 1;
int frontCount = -0x3f3f3f3f;
int endCount = -0x3f3f3f3f;
_Tp arr[MAXSIZE];
void push(_Tp&& element) {
if (full()) {
std::cerr << "Full\n";
return;
}
if (((uint16_t) (end + 1)) == 0)
++endCount;
arr[++end] = element;
}
void push(const _Tp& element) {
if (full()) {
std::cerr << "Full\n";
return;
}
if (((uint16_t) (end + 1)) == 0)
++endCount;
arr[++end] = element;
}
std::optional<_Tp> pop() {
if (empty())
return std::nullopt;
if (((uint16_t) (front + 1)) == 0)
++frontCount;
return arr[++front];
}
bool empty() {
if (endCount > frontCount)
return false;
return (front == end) ? true : false;
}
bool full() {
if (endCount > frontCount && end == front)
return true;
return false;
}
std::size_t size() {
if (full())
return MAXSIZE;
else if (empty())
return 0;
else if (frontCount == endCount)
return (end - front);
else
return (MAXSIZE - front + end);
}
};
相信看完本篇,你也多多少少有收穫,喜歡的可以動動手指點贊加關注!