【數據結構】什麼是堆

堆(heap)

堆(英語:heap)是計算機科學中一類特殊的數據結構的統稱。堆通常是一個可以被看做一棵樹的數組對象。
堆的性質:
1.堆中某個節點的值總是不大於或不小於其父節點的值。
2.堆總是一棵完全二叉樹。

堆的分類

將根節點最大的堆叫做最大堆或大頂堆,根節點最小的堆叫做最小堆或小頂堆。常見的堆有二叉堆、斐波那契堆等。
堆的定義如下:n個元素的序列{k1,k2,ki,…,kn}當且僅當滿足下關係時,稱之爲堆。
(ki <= k2i,ki <= k2i+1)或者(ki >= k2i,ki >= k2i+1), (i = 1,2,3,4…n/2)
若將和此次序列對應的一維數組(即以一維數組作此序列的存儲結構)看成是一個完全二叉樹,則堆的含義表明,完全二叉樹中所有非終端結點的值均不大於(或不小於)其左、右孩子結點的值。由此,若序列{k1,k2,…,kn}是堆,則堆頂元素(或完全二叉樹的根)必爲序列中n個元素的最小值(或最大值)。
每個結點的值都大於或等於其左右孩子結點的值,稱爲大頂堆;或者每個結點的值都小於或等於其左右孩子結點的值,稱爲小頂堆。如下圖:
大堆:
這裏寫圖片描述
小堆:
這裏寫圖片描述

堆的操作

build:建立一個空堆;
insert:向堆中插入一個新元素;
update:將新元素提升使其符合堆的性質;
get:獲取當前堆頂元素的值;
delete:刪除堆頂元素;
heapify:使刪除堆頂元素的堆再次成爲堆。
某些堆實現還支持其他的一些操作,如斐波那契堆支持檢查一個堆中是否存在某個元素。

建堆效率

n個結點的堆,高度d =log2n。根爲第0層,則第i層結點個數爲2i,考慮一個元素在堆中向下移動的距離。大約一半的結點深度爲d-1,不移動(葉)。四分之一的結點深度爲d-2,而它們至多能向下移動一層。樹中每向上一層,結點的數目爲前一層的一半,而子樹高度加一。
這種算法時間代價爲Ο(n)由於堆有log n層深,插入結點、刪除普通元素和刪除最小元素的平均時間代價和時間複雜度都是
Ο(log n)。

關於堆的操作實現

在程序中,堆用於動態分配和釋放程序所使用的對象。在以下情況中調用堆操作:
1.事先不知道程序所需對象的數量和大小。
2.對象太大,不適合使用堆棧分配器。
堆使用運行期間分配給代碼和堆棧以外的部分內存。
傳統上,操作系統和運行時庫隨附了堆實現。當進程開始時,操作系統創建稱爲進程堆的默認堆。如果沒有使用其他堆,則使用進程堆分配塊。語言運行時庫也可在一個進程內創建單獨的堆。(例如,C 運行時庫創建自己的堆。)除這些專用堆外,應用程序或許多加載的動態鏈接庫 (DLL) 之一也可以創建並使用單獨的堆。Win32 提供了一組豐富的 API用於創建和使用專用堆。有關堆函數的優秀教程,請參閱 MSDN 平臺 SDK 節點。
當應用程序或 DLL 創建專用堆時,這些堆駐留於進程空間中並且在進程範圍內是可訪問的。某一給定堆分配的任何數據應爲同一堆所釋放。(從一個堆分配並釋放給另一個堆沒有意義。)
在所有虛擬內存系統中,堆位於操作系統的虛擬內存管理器之上。語言運行時堆也駐留在虛擬內存之上。某些情況下,這些堆在操作系統堆的上層,但語言運行時堆通過分配大的塊來執行自己的內存管理。繞開操作系統堆來使用虛擬內存函數可使堆更好地分配和使用塊。
典型的堆實現由前端分配器和後端分配器組成。前端分配器維護固定大小塊的自由列表。當堆收到分配調用後,它嘗試從前端列表中查找自由塊。如果此操作失敗,則堆將被迫從後端(保留和提交虛擬內存)分配一個大塊來滿足請求。通常的實現具有每個塊分配的開銷,這花費了執行週期,也減少了可用存儲區。
單個全局鎖可防止多線程同時使用堆。此鎖主要用於保護堆數據結構不受多線程的任意訪問。當堆操作過於頻繁時,此鎖會對性能造成負面影響。

代碼實現

#pragma once
template<class T>
class JBMinHeap
{
private:
    //申請堆空間
    T *_minHeap = NULL;
    int _index,_maxSize;
public:
    JBMinHeap(int maxSize) {
        _maxSize = maxSize;
        _minHeap = new T[_maxSize];
        _index = -1;
    }
    JBMinHeap(JBMinHeap &h) {
        _index = h._index;
        _maxSize = h._maxSize;
        _minHeap = new T[_maxSize];
        for (int i = 0;i<_maxSize) {
            *_minHeap[i] = *h._minHeap[i];
        }
    }
    ~JBMinHeap() {
        delete[]_minHeap;
    }
    //獲取整個最小堆的頭部指針
    T * getMinHeap() {
        return _minHeap;
    }
    //判斷堆是不是空的
    bool isEmpty() {
        return _index == -1;
    }
    bool add(T x) {
        if (isFull()) {
            return false;
        }
        _index++;
        _minHeap[_index] = x;
        return true;
    }
    bool isFull() {
        return _index == _maxSize;
    }
    //堆進行向下調整
    void adjustDown(int index);
    //隊進行向上調整
    void adjustUp(int index);
    //建堆運算
    void createMinHeap() {
        if (isEmpty()) {
            return;
        }
        for (int i = (_index-1)/2;i >-1;i--) {//直接從倒數第二層 逐層向下調整
            adjustDown(i);
        }
    }
};
template<class T>
void JBMinHeap<T>::adjustDown(int index) {
    if (isEmpty()) {
        return;
    }
    while (index<_index)
    {
        T temp = _minHeap[index];//將當前索引的位置的值保存下來
        int oneC = 2 * index + 1;//獲取到兩個孩子的位置
        int twoC = 2 * index + 2;
        if (oneC == _index) {//若第一個孩子是整個堆最後一個位置 則直接執行交換操作並結束執行
                _minHeap[index] = _minHeap[oneC];
                _minHeap[oneC] = temp;
                return;
        }
        if (twoC >_index) {//如果第二個孩子的索引位置越界 結束執行
            return;
        }
        if (_minHeap[oneC] <= _minHeap[twoC]) {//正常情況的數據交互執行
            if (temp > _minHeap[oneC]) {
                _minHeap[index] = _minHeap[oneC];
                _minHeap[oneC] = temp;
                index = oneC;
            }
            else {//如果該處索引值已經是比兩個孩子小 則結束循環
                index = _index;
            }
        }
        else 
        {
            if (temp > _minHeap[twoC]) {
                _minHeap[index] = _minHeap[twoC];
                _minHeap[twoC] = temp;
                index = twoC;
            }
            else 
            {
                index = _index;
            }
        }
    }
}
template<class T>
void JBMinHeap<T>::adjustUp(int index) {
    if (index > _index) {//大於堆的最大值直接return
        return;
    }
    while (index>-1)
    {
        T temp = _minHeap[index];
        int father = (index - 1) / 2;
        if (father >= 0) {//若果索引沒有出界就執行想要的操作
            if (temp < _minHeap[father]) {
                _minHeap[index] = _minHeap[father];
                _minHeap[father] = temp;
                index=father;
            }
            else {//若果已經是比父親大 則直接結束循環
                index = -1;
            }
        }
        else//出界就結束循環
        {
            index = -1;
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章