數據結構筆記3.4
與普通的隊列不同,優先級隊列並不一定按照FIFO(先進先出)的原則對數據進行操縱,而是每次從隊列取出具有最高優先權的元素。打個比方,你現在有一堆任務,而你只能一項一項的完成,一種方式是哪項任務先來的你完成哪個;另一種方式是你考慮了各項任務的輕重緩急,然後給他們各自一個優先級序號(就是執行先後次序的序號),你按照這個執行序號來完成每一項任務。這其實就好比是,兩種隊列的差別。
優先級隊列包括最小優先級隊列和最大優先級隊列(就是出隊元素是按由小到大出,還是由大到小出),這裏實現的是最小優先級隊列,最大的與之類似。
而爲什麼說是基於數組的優先級隊列,當然,這是因爲還可以利用堆(heap)堆優先級隊列進行存儲表示,這在後續會有相應的文章。這裏給出用數組存儲優先級隊列的代碼,如有不當之處,歡迎各位指正!
注意的問題:在實現這個類的時候,關鍵的函數是adjust,他在每次進入隊新元素的時候都會調用一次,所以其實每次都是在一個已經有序的數列中插入新的元素(注意區分與數組排序的不同),所以在實現的時候也會有不同。
算法性能分析:在對數據進行操作的時候涉及到進隊函數Insert和出隊函數RemoveMin,進隊函數的的時間複雜度(爲保持數據始終有序,需要用adjust函數對整個數組的元素比較和移動)爲:最好的情況進入隊列的數據就是整個隊中的最大值,這時候不需要移動,最壞的情況是進入的是最小值,這時候需要比較並移動n + 1次(即元素的數量次)。而每次出隊一個數據,也需要其後面的所有數據都向前移動進行填補(節約數組空間)。綜上,算法的時間複雜度爲O(n)。後續我會介紹用heap實現,會將時間複雜度提升到對數級別。下面貼出代碼:
基於數組的優先級隊列模板class
//數據結構——基於數組的優先級隊列
#include <iostream>
#include <cassert>
using namespace std;
const int DefaultPQSize = 50; //優先級隊列數組的默認長度
//優先級隊列類定義
template<class T>
class PQueue {
public:
PQueue(int sz = DefaultPQSize); //構造函數
~PQueue(); //析構函數
bool Insert(const T & x); //將新元素x插入到隊尾
bool RemoveMin(T & x); //將隊頭元素刪除
bool GetFront(T & x) const; //讀取隊頭元素,具有優先權最小的值
void MakeEmpty(); //設置優先級隊列爲NULL
bool IsEmpty() const; //判斷優先級隊列是否爲NULL
bool IsFull() const; //判斷優先級隊列是否已FULL
int GetSize() const; //求優先級隊列中元素的個數
void Output(); //隊列輸出函數
protected:
T *pqElements; //優先級隊列數組
int count; //當前元素的個數(計數器)
int maxSize; //優先級隊列最大可容納元素的個數
void adjust(); //隊列調整
};
//類的函數實現
template<class T>
PQueue<T>::PQueue(int sz) {
//構造函數,建立一個最大具有maxSize元素的NULL的優先級隊列
maxSize = sz;
count = 0;
pqElements = new T[maxSize]; //開闢隊列空間
assert(pqElements != NULL); //斷言:檢驗是否空間分配成功
}
template<class T>
PQueue<T>::~PQueue() {
//析構函數,釋放程序所佔用的資源
delete[] pqElements;
}
template<class T>
bool PQueue<T>::Insert(const T & x) {
//若優先級隊列不FULL,則將元素插入到隊列的隊尾,否則返回false
if (count == maxSize) { //隊列已滿返回false
return false;
}
pqElements[count] = x; //隊列不滿,將x插入到隊尾
count++;
adjust(); //對隊列進行權值調整
return true;
}
template<class T>
void PQueue<T>::adjust() {
//將隊列元素按其優先級大小調整,保持所有元素按照從小到大有序
//始終向一個有序數列中插入元素,注意這裏並不是對整個數組排序
T temp = pqElements[count - 1]; //新插入的元素賦值給temp
for (int j = count - 2; j >= 0; j --) {
if (pqElements[j] <= temp) {
break; //因爲原來的序列已經是有序的,所以只與最後一個元素比較即可
}
else {
pqElements[j + 1] = pqElements[j];
pqElements[j] = temp; //插入到合適的位置,並繼續循環
}
}
}
template<class T>
bool PQueue<T>::RemoveMin(T & x) {
//若隊列不爲NULL則函數返回具有最大優先權(即最小值)的值,
//並且刪除該元素
if (0 == count) { //優先級隊列爲NULL,返回錯誤
return false;
}
x = pqElements[0];
//運用元素覆蓋的方式刪除第一個元素(注意下標爲0)
for (int i = 0; i < count - 1; i ++) {
pqElements[i] = pqElements[i + 1];
}
count--; //隊列元素總數目減1
return true;
}
template<class T>
bool PQueue<T>::GetFront(T & x) const {
//若優先級隊列不爲NULL,則返回最大優先級的元素
if (0 == count) {
return false;
}
x = pqElements[0];
return true;
}
template<class T>
bool PQueue<T>::IsEmpty() const{
if (0 == count) {
return true;
}
else {
return false;
}
}
template<class T>
bool PQueue<T>::IsFull() const {
if (count == maxSize) {
return true;
}
else {
return false;
}
}
template<class T>
void PQueue<T>::MakeEmpty() {
//設置優先級隊列爲NULL
count = 0;
}
template<class T>
int PQueue<T>::GetSize() const {
return count;
}
template<class T>
void PQueue<T>::Output() {
//按照優先級輸出隊列中的所有元素
for (int i = 0; i < count; i ++) {
cout << pqElements[i] << ' ';
}
cout << endl;
}
Main函數演示代碼:
int main()
{
PQueue<int> pqueue;
pqueue.Insert(5);
pqueue.Insert(3);
pqueue.Insert(4);
pqueue.Insert(2);
pqueue.Insert(7);
pqueue.Output(); //此時可以看到隊列已經有序
int min_1, min_2;
pqueue.RemoveMin(min_1);
pqueue.RemoveMin(min_2);
pqueue.Output(); //此時可以看到出隊的並非最先插入的元素,而是具有最高優先級的元素
system("pause");
return 0;
}
運行效果圖: