數據結構——二叉堆和優先隊列

一、概念

隊列和堆都是一種數據結構,優先隊列是用二叉堆實現的。優先隊列和堆常用語輔助實現其它算法,例如數據壓縮赫夫曼編碼算法、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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章