資料結構實現 6.4:優先佇列_基於連結串列實現(C++版)
阿新 • • 發佈:2018-11-27
資料結構實現 6.4:優先佇列_基於連結串列實現(C++版)
1. 概念及基本框架
6.3 中通過 動態陣列 實現了 優先佇列 ,這一節我們通過連結串列來實現優先佇列。
優先佇列 是 佇列 的一種,所以有佇列的基本特性:
1.佇列
2.入隊 操作只能從 隊尾 進行,出隊 操作只能從 隊頭 進行。
3.先 入隊 的先 出隊 ,即 先進先出(First In First Out),FIFO 。
還有一個隱含特性,佇列可以自行 擴容(縮容),而不需要使用者關心,很顯然,連結串列符合這個要求。
首先使用一個由 純虛擬函式 構成的 抽象類 作為一個佇列介面,介面內定義一些佇列的基本操作。具體程式碼如下:
template <class T>
class Queue{
public:
virtual int size() = 0;
virtual bool isEmpty() = 0;
virtual void print() = 0;
//入隊操作
virtual void enqueue(T num) = 0;
//出隊操作
virtual void dequeue() = 0;
//獲得隊首元素
virtual T front() = 0;
};
下面只需要通過繼承 抽象類,並且重寫 純虛擬函式 ,就可以完成 優先佇列 的實現。優先佇列類的框架如下:
template <class T>
class LinkedListPriorityQueue{
...
private:
LinkedList<T> list;
};
同樣,這裡為了避免重複設計就可以相容更多資料型別,引入了 泛型
這裡的 list 表示一個 連結串列 ,同樣,為了保護資料,變數設定為 private 。
注:這裡沒有顯式的給出建構函式,因為子類中除了連結串列物件之外沒有特別需要初始化的東西。編譯器會預設先呼叫 連結串列 類(即父類)的建構函式,再去呼叫 優先佇列 類(即子類)的建構函式。
實現了前面的程式之後,接下來就是一個佇列的增、刪、查以及一些其他基本操作,接下來利用程式碼去實現。
2. 基本操作程式實現
2.1 入隊操作
template <class T>
class LinkedListPriorityQueue{
public:
...
//入隊操作
void enqueue(T num){
list.addFirst(num);
}
...
};
2.2 出隊操作
template <class T>
class LinkedListPriorityQueue{
public:
...
//出隊操作
void dequeue(){
if (list.isEmpty()){
cout << "出隊失敗!" << endl;
return;
}
list.remove(findMax());
}
...
private:
int findMax(){
int index = 0;
for (int i = 0; i < list.size(); ++i){
if (list.get(i) > list.get(index)){
index = i;
}
}
return index;
}
...
這裡利用了一次遍歷查詢最大元素。
2.3 查詢操作
template <class T>
class LinkedListPriorityQueue{
public:
...
//獲得隊首元素
T front(){
return list.getFirst();
}
private:
int findMax(){
int index = 0;
for (int i = 0; i < list.size(); ++i){
if (list.get(i) > list.get(index)){
index = i;
}
}
return index;
}
...
};
這裡利用了一次遍歷查詢最大元素。
2.4 其他操作
優先佇列還有一些其他的操作,包括 佇列大小 等的查詢等操作。
template <class T>
class LinkedListPriorityQueue{
public:
int size(){
return list.size();
}
bool isEmpty(){
return list.isEmpty();
}
void print(){
if (list.isEmpty()){
return;
}
cout << "LinkedListPriorityQueue: ";
cout << "Size = " << list.size() << endl;
cout << "front [";
cout << list.get(findMax()) << "...";
cout << "] rear" << endl;
}
...
};
3. 演算法複雜度分析
這裡入隊使用直接插入,出隊時遍歷查詢的方法實現優先佇列。
3.1 入隊操作
函式 | 最壞複雜度 | 平均複雜度 |
---|---|---|
enqueue | O(1) | O(1) |
3.2 出隊操作
函式 | 最壞複雜度 | 平均複雜度 |
---|---|---|
dequeue | O(n) | O(n/2) = O(n) |
3.3 查詢操作
函式 | 最壞複雜度 | 平均複雜度 |
---|---|---|
front | O(n) | O(n/2) = O(n) |
總體情況:
操作 | 時間複雜度 |
---|---|
增 | O(1) |
刪 | O(n) |
查 | O(n) |
也可以採用入隊時遍歷放入適當位置,出隊出隊頭。
4. 完整程式碼
程式完整程式碼(這裡使用了標頭檔案的形式來實現類)如下:
優先佇列介面函式一覽:
函式宣告 | 函式型別 | 函式功能 |
---|---|---|
int size() | public | 返回二叉堆的大小 |
bool isEmpty() | public | 返回優先佇列是否為空(空為true) |
void print() | public | 列印輸出優先佇列隊頭 |
void enqueue(T) | public | 入隊元素到優先佇列 |
void dequeue() | public | 從優先佇列中出隊一個元素 |
T front() | public | 獲得優先佇列隊頭元素 |
抽象類 介面程式碼:
#ifndef __QUEUE_H__
#define __QUEUE_H__
template <class T>
class Queue{
public:
virtual int size() = 0;
virtual bool isEmpty() = 0;
virtual void print() = 0;
//入隊操作
virtual void enqueue(T num) = 0;
//出隊操作
virtual void dequeue() = 0;
//獲得隊首元素
virtual T front() = 0;
};
#endif
這裡不再給出連結串列程式碼。
優先佇列類 程式碼:
#ifndef __LINKEDLISTPRIORITYQUEUE_H__
#define __LINKEDLISTPRIORITYQUEUE_H__
#include "Queue.h"
#include "LinkedList.h"
template <class T>
class LinkedListPriorityQueue{
public:
int size(){
return list.size();
}
bool isEmpty(){
return list.isEmpty();
}
void print(){
if (list.isEmpty()){
return;
}
cout << "LinkedListPriorityQueue: ";
cout << "Size = " << list.size() << endl;
cout << "front [";
cout << list.get(findMax()) << "...";
cout << "] rear" << endl;
}
//入隊操作
void enqueue(T num){
list.addFirst(num);
}
//出隊操作
void dequeue(){
if (list.isEmpty()){
cout << "出隊失敗!" << endl;
return;
}
list.remove(findMax());
}
//獲得隊首元素
T front(){
return list.getFirst();
}
private:
int findMax(){
int index = 0;
for (int i = 0; i < list.size(); ++i){
if (list.get(i) > list.get(index)){
index = i;
}
}
return index;
}
private:
LinkedList<T> list;
};
#endif