一、概念
隊列和堆都是一種數據結構,優先隊列是用二叉堆實現的。優先隊列和堆常用語輔助實現其它算法,例如數據壓縮赫夫曼編碼算法、Dijkstra最短路徑算法、Prim最短生成樹算法,優先隊列還可以實現事件模擬、選擇問題,操作系統的線程調度也使用優先隊列。
優先隊列
特點:元素正常入隊,按照優先級大小出隊。
實現方式:有序表或無序表(即數組,或順序表和鏈表)、二叉樹、AVL平衡二叉樹、堆和二叉堆。使用普通的表實現優先隊列的性能比較低,有序表插入爲O(1),無序表取出爲O(n),另外使用普通二叉樹實現優先隊列只能保證平均時間爲O(log n),但最差情況是O(n),AVL樹則需要複雜的無關操作,而堆可以實現性能較好的優先隊列,取出效率可達到O(1),一般的操作都能達到O(log n)。
二叉堆
特點:二叉堆是完全二叉樹,分爲最大堆和最小堆兩種類型,最小堆中根結點的關鍵字是其它所有結點關鍵字的最小值,最大堆則是最大值。
表示方式:完全二叉樹是二叉堆的邏輯表示方式,數組是二叉堆的物理表示,也是計算機中的實際形式。
應用:堆排序複雜度O(nlogn)、優先隊列
// 二叉堆構造函數,capacity: 容量(決定二叉堆大小),compare: 比較函數(決定是最大堆還是最小堆),array: 數組
function BinaryHeap(capacity, compare, array){
if(array){
this.array = array.concat();
this.size = array.length;
if(capacity < 3)
this.capacity = this.size * 2;
else
this.capacity = capacity;
this.compare = compare;
}
else{
if(capacity < 3)
return null;
this.array = new Array(capacity);
this.size = 0;
this.capacity = capacity;
this.compare = compare;
}
}
// 檢查二叉堆是否已空
BinaryHeap.prototype.isEmpty = function(){
return this.size == 0;
}
// 獲取結點i的父結點
BinaryHeap.prototype.parent = function(i){
return Math.floor((i - 1) / 2);
}
// 獲取結點i的左兒子
BinaryHeap.prototype.left = function(i){
return 2 * i + 1;
}
// 獲取結點i的右兒子
BinaryHeap.prototype.right = function(i){
return 2 * i + 2;
}
// 根據數組元素的索引進行交換
BinaryHeap.prototype.swap = function(i, j){
var temp = this.array[i];
this.array[i] = this.array[j];
this.array[j] = temp;
}
// 二叉堆push操作,往堆中添加一個新元素
BinaryHeap.prototype.push = function(T){
// 沒有容量了
if(this.size == this.capacity){
console.log("overflow: could not push key.");
return;
}
// 錯位,數組0索引處是樹的根節點,也可以不給0賦值,把數組索引和樹的位置對應
// 只要把數組長度加1
this.size++;
var i = this.size - 1;
this.array[i] = T;
// 上浮操作,由下而上檢查,新元素和它的父節點比較
while(i != 0 && this.compare(this.array[i], this.array[this.parent(i)])){
// 交換新元素和父節點位置
this.swap(i, this.parent(i));
// 把父節點索引給i,繼續向上比較
i = this.parent(i);
}
}
// 下沉操作,修復二叉堆的性質,從根節點自上而下檢查,需要比較父節點和它兩個子節點
BinaryHeap.prototype.heapify = function(i){
var left = this.left(i);
var right = this.right(i);
// small存儲值比較小的節點
var small = i;
// 順序比較 父元素 左子元素 右子元素
// 比較父元素和左子元素
if(left < this.size && this.compare(this.array[left], this.array[i]))
small = left;
// 比較左子元素和右子元素
if(right < this.size && this.compare(this.array[right], this.array[small]))
small = right;
// 父元素的值改變,交換
if(small != i){
this.swap(i, small);
// 繼續下浮,恢復堆結構
this.heapify(small);
}
}
// 獲取堆頂元素
BinaryHeap.prototype.top = function(){
return this.array[0];
}
// 釋放堆頂元素,將最後一個元素替換堆頂元素,然後進行下沉堆化操作
BinaryHeap.prototype.pop = function(){
if(this.size <= 0){
console.log("heap is empty.");
return null;
}
if(this.size == 1){
this.size--;
return this.array[0];
}
var root = this.array[0];
this.array[0] = this.array[this.size - 1];
this.size--;
// 從頂部下浮恢復結構
this.heapify(0);
return root;
}
// 改變第i個結點的值
BinaryHeap.prototype.decreaseKey = function(i, NT){
this.array[i] = NT;
// 判斷改變過後是否還符合二叉堆性質
while(i != 0 && this.compare(this.array[i], this.array[this.parent(i)])){
this.swap(i, this.parent(i));
i = this.parent(i);
}
}
// 刪除元素
BinaryHeap.prototype.delete = function(i){
if(this.size <= 0){
console.log("heap is empty.");
return;
}
if(this.size == 1){
this.size--;
return;
}
// 把最後一個值替換到要刪除的位置上
this.array[i] = this.array[this.size - 1];
// 然後數組長度減一
this.size--;
// 再下沉,恢復結構
this.heapify(i);
}
// 構建堆,用於使用一個數組構建堆的時候進行堆化操作
BinaryHeap.prototype.buildHeap = function(){
// 從數組中間的索引位置開始下浮
for(var i = Math.floor(this.size / 2);i >= 0;i--)
this.heapify(i);
}