高效利用佇列的空間

ChebyshevTST發表於2023-11-11

  大家都知道佇列是可以用陣列來模擬的,可以先開闢一段定長的陣列空間,然後分別使用兩個變數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);
	}
};

  相信看完本篇,你也多多少少有收穫,喜歡的可以動動手指點贊加關注!  

相關文章