複製鏈接
請複製以下鏈接發送給好友

(數據結構)

鎖定
堆(Heap)是計算機科學中一類特殊的數據結構,是最高效的優先級隊列。堆通常是一個可以被看作一棵完全二叉樹的數組對象。
中文名
外文名
heap
定    義
一類數據結構的統稱

釋義

堆(heap)是計算機科學中一類特殊的數據結構的統稱。堆通常是一個可以被看做一棵樹的數組對象。堆總是滿足下列性質:
  • 堆中某個結點的值總是不大於或不小於其父結點的值;
  • 堆總是一棵完全二叉樹。
根結點最大的堆叫做最大堆或大根堆,根結點最小的堆叫做最小堆或小根堆。常見的堆有二叉堆斐波那契堆等。
堆的物理結構本質上是順序存儲的,是線性的。但在邏輯上不是線性的,是完全二叉樹的這種邏輯儲存結構。 堆的這個數據結構,裏面的成員包括一維數組,數組的容量,數組元素的個數,有兩個直接後繼。
堆的定義如下:n個元素的序列{k1,k2,ki,…,kn}當且僅當滿足下關係時,稱之為堆。
(
)或者(
), (
)
若將和此次序列對應的一維數組(即以一維數組作此序列的存儲結構)看成是一個完全二叉樹,則堆的含義表明,完全二叉樹中所有非終端結點的值均不大於(或不小於)其左、右孩子結點的值。由此,若序列{k1,k2,…,kn}是堆,則堆頂元素(或完全二叉樹的根)必為序列中n個元素的最小值(或最大值)。

STL

堆的STL包含於<algorithm>頭文件中。
堆的STL支持以下的基本操作:
make_heap(first, last, comp);
建立一個空堆;
向堆中插入一個新元素;
top_heap(first, last, comp);
獲取當前堆頂元素的值;
sort_heap(first, last, comp);
對當前堆進行排序;

算法思想

不必將值一個個地插入堆中,通過交換形成堆。假設一個小根堆的左、右子樹都已是堆,並且根的元素名為
,其左右子結點為
,這種情況下,有兩種可能:
(1)
並且
,此時堆已完成;
(2)
或者
,此時
應與兩個子女中值較小的一個交換,結果得到一個堆,除非
仍然大於其新子女的一個或全部的兩個。這種情況下,我們只需簡單地繼續這種將
“拉下來”的過程,直至到達某一個層使它小於它的子女,或者它成了葉結點

篩選法

首先將要排序的所有關鍵碼放到一棵完全二叉樹的各個結點中(這時的完全二叉樹並不具備堆的特性)。顯然,所有的結點
都沒有子女結點,因此以這樣的
為根的子樹已經是堆,然後從 的結點Ki開始,逐步把以為根的子樹排成堆,直到以K0為根的子樹排成堆,就完成了建堆過程。
在考慮將以
為根的子樹排成堆時,以
為根的子樹已經是堆,所以這時如果有
,則不必改變任何結點的位置,以
為根的子樹就已經是堆;否則就要適當調整子樹中結點的位置以滿足堆的定義。由於Ki的左、右子樹都已經是堆,根結點是堆中最小的結點,所以調整後
的值必定是原來
中較小的一個。假設
較小,將
交換位置,這樣調整後
,並且以
為根的子樹原來已經是堆,不必再作任何調整,只有以
為根的子樹由於
的值已經發生變化(與
交換了),所以有可能不滿足堆的定義(當
的左、右子樹已經是堆)。這時可重複上述過程,考慮將
以為根的子樹排成堆。如此一層一層遞推下去,最多可以一直進行到樹葉。由於每步都保證將子樹中最小的結點交換到子樹的根部,所以這個過程是不會反饋的。它就像過篩一樣,把最小的關鍵碼一層一層選擇出來。

建堆效率

個結點的堆,高度
。根為第0層,則第
層結點個數為
,考慮一個元素在堆中向下移動的距離,這種算法時間代價為
由於堆有
層深,插入結點、刪除普通元素和刪除最小元素平均時間代價和時間複雜度都是

代碼實現

c++實現
#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//出界就結束循環1
        {
            index = -1;
        }
    }
}

VB.net實現
Public Class Priority_Queue
Friend Element() As Integer
Friend point As Integer ReadOnly Property Top As Integer
Get
Try
Return Element(1)
Catch ex As Exception
Return 0
End Try
End Get
End Property
ReadOnly Property Size As Integer
Get
Return point
End Get
End Property
ReadOnly Property Empty As Boolean
Get
If point = 0 Then Return True
Return False
End Get
End Property Sub push(ByVal _ele_val As Integer)
point += 1
Element(point) = _ele_val
Dim Now As Integer = point
Dim Nxt As Integer = Now / 2
While (Now <> 1 AndAlso Element(Now) > Element(Nxt))
Swap(Element(Now), Element(Nxt))
Now = Nxt
Nxt /= 2
End While
End Sub
Sub pop()
Dim now As Integer = 1 Element(1) = Element(point)
point -= 1 While (1) Dim left As Integer = 0
Dim right As Integer = 0 If (now * 2 <= point) Then
left = Element(now * 2)
End If
If (now * 2 + 1 <= point) Then
right = Element(now * 2 + 1)
End If If (right = left And right = 0) Then
Exit Sub
End If If (Element(now) < left OrElse Element(now) < right) Then
If left > right Then
Swap(Element(now), Element(now * 2))
now *= 2
Else
Swap(Element(now), Element(now * 2 + 1))
now *= 2
now += 1
End If
Else
Exit While
End If
End While End Sub Sub New(ByVal _size As Integer)
ReDim Element(_size)
point = 0
End Sub
Protected Overrides Sub Finalize()
MyBase.Finalize()
End Sub End Class