1. 程式人生 > >資料結構實現 6.2:優先佇列_基於最大二叉堆實現(C++版)

資料結構實現 6.2:優先佇列_基於最大二叉堆實現(C++版)

資料結構實現 6.2:優先佇列_基於最大二叉堆實現(C++版)

1. 概念及基本框架

佇列 是一種現實中很常見的資料結構,但有時候佇列排序不僅僅依靠入隊時間來決定,而是不同的元素有不同的優先順序。顯然,通過 6.1 中的 最大二叉堆

可以很容易實現 優先佇列

優先佇列

優先佇列佇列 的一種,所以有佇列的基本特性:
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 PriorityQueue{
	...
private:
MaxHeap<T> heap; };

同樣,這裡為了避免重複設計就可以相容更多資料型別,引入了 泛型 ,即 模板 的概念。(模板的關鍵字是 classtypename
這裡的 heap 表示一個 最大二叉堆 ,同樣,為了保護資料,變數設定為 private
注:這裡沒有顯式的給出建構函式,因為子類中除了最大二叉堆物件之外沒有特別需要初始化的東西。編譯器會預設先呼叫 最大二叉堆 類(即父類)的建構函式,再去呼叫 優先佇列 類(即子類)的建構函式。
實現了前面的程式之後,接下來就是一個佇列的增、刪、查以及一些其他基本操作,接下來利用程式碼去實現。

2. 基本操作程式實現

2.1 入隊操作

template <class T>
class PriorityQueue{
public:
	...
	//入隊操作
	void enqueue(T num){
		heap.add(num);
	}
	...
};

2.2 出隊操作

template <class T>
class PriorityQueue{
public:
	...
	//出隊操作
	void dequeue(){
		heap.extractMax();
	}
	...
};

2.3 查詢操作

template <class T>
class PriorityQueue{
public:
	...
	//獲得隊首元素
	T front(){
		return heap.findMax();
	}
	...
};

2.4 其他操作

優先佇列還有一些其他的操作,包括 佇列大小 等的查詢等操作。

template <class T>
class PriorityQueue{
public:
	int size(){
		return heap.size();
	}
	bool isEmpty(){
		return heap.isEmpty();
	}
	void print(){
		if (heap.isEmpty()){
			return;
		}
		cout << "PriorityQueue: ";
		cout << "Size = " << heap.size() << endl;
		cout << "front [";
		cout << heap.findMax() << "...";
		cout << "] rear" << endl;
	}
	...
};

3. 演算法複雜度分析

因為這裡直接呼叫的最大二叉堆的介面函式,所以基本操作的時間複雜度和最大二叉堆操作的時間複雜度相同。

3.1 入隊操作

函式 最壞複雜度 平均複雜度
enqueue O(logn) O(logn)

3.2 出隊操作

函式 最壞複雜度 平均複雜度
dequeue O(logn) O(logn)

3.3 查詢操作

函式 最壞複雜度 平均複雜度
front O(1) O(1)

總體情況:

操作 時間複雜度
O(logn)
O(logn)
O(1)

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 __MAXHEAP_H__
#define __MAXHEAP_H__

#include "Array.h"

template <class T>
class MaxHeap{
public:
	int size(){
		return arr.size();
	}
	bool isEmpty(){
		return arr.isEmpty();
	}
	void add(T num){
		arr.addLast(num);
		siftUp(arr.size() - 1);
	}
	T findMax(){
		if (arr.size() == 0){
			cout << "二叉堆為空!" << endl;
			return NULL;
		}
		return arr.get(0);
	}
	T extractMax(){
		T res = findMax();
		swap(0, arr.size() - 1);
		arr.removeLast();
		siftDown(0);
		return res;
	}
private:
	int parent(int index){
		if (index <= 0 || index >= arr.size()){
			return NULL;
		}
		return (index - 1) / 2;
	}
	int leftChild(int index){
		return index * 2 + 1;
	}
	int rightChild(int index){
		return (index + 1) * 2;
	}
	void swap(int i, int j){
		if (i < 0 || i >= arr.size() || j < 0 || j >= arr.size()){
			return;
		}
		T t = arr.get(i);
		arr.set(i, arr.get(j));
		arr.set(j, t);
	}
	void siftUp(int index){
		while (index && arr.get(index) > arr.get(parent(index))){
			swap(index, parent(index));
			index = parent(index);
		}
	}
	void siftDown(int index){
		while (leftChild(index) < arr.size()){
			int left = leftChild(index);
			if (left + 1 < arr.size() && arr.get(left + 1) > arr.get(left)){
				left++;
			}
			if (arr.get(index) >= arr.get(left)){
				break;
			}
			swap(index, left);
			index = left;
		}
	}
private:
	Array<T> arr;
};

#endif

優先佇列類 程式碼:

#ifndef __PRIORITYQUEUE_H__
#define __PRIORITYQUEUE_H__

#include "Queue.h"
#include "MaxHeap.h"

template <class T>
class PriorityQueue{
public:
	int size(){
		return heap.size();
	}
	bool isEmpty(){
		return heap.isEmpty();
	}
	void print(){
		if (heap.isEmpty()){
			return;
		}
		cout << "PriorityQueue: ";
		cout << "Size = " << heap.size() << endl;
		cout << "front [";
		cout << heap.findMax() << "...";
		cout << "] rear" << endl;
	}
	//入隊操作
	void enqueue(T num){
		heap.add(num);
	}
	//出隊操作
	void dequeue(){
		heap.extractMax();
	}
	//獲得隊首元素
	T front(){
		return heap.findMax();
	}
private:
	MaxHeap<T> heap;
};

#endif