二叉堆


一、定義

二叉堆是堆的一種,使用完全二叉樹來實現。所謂完全二叉樹,即高度為n的二叉樹,其前n-1層必須被填滿,第n層也要從左到右順序填滿。在二叉堆中,所有非終端結點的值均不大於(或不小於)其左右孩子的值。若非終端結點的值均不大於其左右孩子結點的值,這樣的二叉堆叫做小根堆(下圖中(b)),小根堆根結點的值是該堆中所有結點的最小值;同樣的,當所有非終端結點的值都不小於其左右孩子的值時,這樣的對叫做大根堆(下圖中(a)),大根堆根結點的值為改堆所有結點的最大值。利用堆的此性質,可以實現堆排序。

 

說明:小根堆和大根堆的實現沒有太大的區別,所以下面以小根堆為例。

二、二叉堆的操作

堆一般使用數組來構建,假設為數組a[],結點通常存儲在a[1],這樣對於下標為k的結點a[k]來說,其左孩子的下標為2*k,右孩子的下標為2*k+1。

1、插入結點到堆中.

由於小根堆是由數組實現的完全二叉樹,所以插入的位置應該是完全二叉樹的最后一個位置(如下圖所示),對於小根堆來講,需要滿足兩個性質:(1)堆為完全二叉樹;(2)堆中每個結點的值都不大於其左右結點的值。插入結點可能會破壞這兩條性質,所以在插入結點后需要對堆進行調整。調整方法為:將插入的結點與其父結點比較,若小於其父結點的值,則交換兩者。重復此操作,直至該結點不比其父結點小,或者該結點成為根結點。可以通過插入結點到一個已經存在的堆中,也可以通過不斷插入結點來構建一個堆。

 

 

2、刪除堆頂元素(堆排序)

刪除堆頂元素(根結點)后,會得到左右兩棵子樹,此時將堆中最后一個元素移到堆頂,然后自上而下調整,將該結點與左右孩子結點比較,此時會有三種情況:

(1)結點的左右孩子均為空,此時調整結束;

(2)結點只有左孩子,此時將該結點與其左孩子比較。若結點大於其左孩子,則兩者交換,否則調整結束;

(3)結點左右孩子都非空,則將該結點與左右孩子之間的較小者比較,若小於則交換,否則調整結束;

重復此過程,直到該結點不大於其左右孩子結點,或者該結點為葉子結點。

3、實現代碼

#include <algorithm>
#include <iostream>
using namespace std;

class MinHeap
{
private:
    int* heap;        //存儲堆
    int cur;          //堆中結點個數

    /*插入結點后,向上調整*/
    void adjustUp();    

    /*刪除結點后,向下調整*/
    void adjustDown(int idx);
public:
    MinHeap();
    ~MinHeap();

    /*插入值為val的結點*/
    void insert(int val);

    /*返回最小值並刪除最小值結點*/
    int deleteMin();
};

MinHeap::MinHeap()
{
    heap = new int[20];
    cur = 0;
}

MinHeap::~MinHeap()
{
    delete[] heap;
}

/*插入值為val的結點*/
void MinHeap::insert(int val)
{
    heap[++cur] = val;
    adjustUp();
}

/*插入結點后,向上調整*/
void MinHeap::adjustUp()
{
    int idx = cur;
    int pIdx = cur / 2;
    while (pIdx > 0 && heap[idx] < heap[pIdx])
    {
        swap(heap[idx], heap[pIdx]);
        idx = pIdx;
        pIdx = pIdx / 2;
    }
}

/*返回最小值並刪除最小值結點*/
int MinHeap::deleteMin()
{
    int minVal = heap[1];
    heap[1] = heap[cur--];
    adjustDown(1);
    return minVal;
}

/*刪除結點后,向下調整*/
void MinHeap::adjustDown(int idx)
{
    if (idx > cur)
        return;
    int lIdx = idx * 2;
    int rIdx = idx * 2 + 1;

    int minIdx = 0;
    if (lIdx > cur)            //無左右孩子
        return;    
    else if (rIdx > cur)       //只有左孩子
        minIdx = lIdx;
    else minIdx = heap[lIdx] < heap[rIdx] ? lIdx : rIdx;    //左右孩子均非空

    if (heap[idx] > heap[minIdx])
    {
        swap(heap[idx], heap[minIdx]);
        adjustDown(minIdx);
    }
    else return;
}

int main()
{
    int a[] = { 5,1,3,4,2 };
    int len = sizeof(a) / sizeof(a[0]);
    
    /*插入結點構造最小堆*/
    MinHeap* minHeap = new MinHeap();
    for (int i = 0;i < len; i++)
        minHeap->insert(a[i]);

    /*輸出堆頂結點的值*/
    for (int i = 0;i < len;i++)
        cout << minHeap->deleteMin() << " ";
    cout << endl;
    
    return 0;
}

結果:

1 2 3 4 5

三、參考

1、http://www.cnblogs.com/vamei/archive/2013/03/20/2966612.html

2、嚴蔚敏、吳偉民《數據結構》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM