1. 程式人生 > >C++資料結構 18 堆

C++資料結構 18 堆

堆:使用陣列實現

堆的實現通過構造二叉堆(binary heap),實為二叉樹的一種;由於其應用的普遍性,當不加限定時,均指該資料結構的這種實現。這種資料結構具有以下性質。

任意節點小於(或大於)它的所有後裔,最小元(或最大元)在堆的根上(堆序性)。 
堆總是一棵完全樹。即除了最底層,其他層的節點都被元素填滿,且最底層儘可能地從左到右填入。

將根節點最大的堆叫做最大堆或大根堆,根節點最小的堆叫做最小堆或小根堆。

簡單來說,堆的儲存結構是陣列,邏輯結構是一棵完全二叉樹,通過索引的方式表示二叉樹父節點和孩子節點的關係,父節點大於孩子節點叫大根堆,反之叫小根堆.堆支援的主要操作有:入堆,出堆,調堆,堆排序,其中插入操作複雜度為O(logn)O(logn),因此堆排序的複雜度是O(nlogn)O(nlogn)
實現
主要實現幾個操作push,pop

1.插入:
也就是,首先插入一定是插入到完全二叉樹的最右下,對應陣列中,也就是back()的位置,那麼為了滿足堆的性質,就需要不斷的上溯,也就是將新插入的元素往上到合適的位置.這裡沿用STL中的實現方式,並不是直接的swap,而是先生成一個HoleInex也就是產生一個空洞,這個空洞就是待插入的位置,但是這個空洞需要不斷的上溯到合適位置,然後將新值插入即可.

2.彈出:
彈出堆中的最大值/最小值,對應的就是堆頂元素,陣列索引位置為0或者1(取決於實現)。這個時候將堆頂元素放到末尾(覆蓋最後一個元素),堆頂的這個位置就變成了空洞了,此時需要將原來的末尾的元素插入到這個空洞中,就需要將這個空洞下沉到合適位置,然後將元素插入,並且堆大小減1

3.排序:
pop操作每次都會彈出最大或者最小的元素到堆尾,那麼執行n-1次彈出操作,陣列就有序了。
 

堆可分為大頂堆和小頂堆

大頂堆就是最大的元素為根元素,小頂堆最小的元素是根元素

堆的操作:

   插入新節點-向上滲透(先把節點插入到最後,和父節點依次比大小)

   刪除根節點-向下滲透(先把最後一個節點放置到根節點,再依次比較大小)

#ifndef __MAXHEAP_H__
#define __MAXHEAP_H__
#include <iostream>

using namespace std;

template<class T>
class MaxHeap
{
public:
    MaxHeap(int mx=10);   //建構函式
    virtual ~MaxHeap();   //解構函式

    bool isEmpty();        //是否為空
    void Push(const T&);   //插入資料
    void Pop();            //刪除資料
    const T& Top()const;   //檢視根節點資料
private:
    T*  HeadArry;        //陣列
    int maxSize;        //最大的大小
    int currentSize;    //當前大小

    void trickUp(int index); //向上滲透
    void trickDown(int index);//向下滲透
};


template<class T>
MaxHeap<T>::MaxHeap(int mx)//建構函式
{
    if(mx<1)
        throw "Error";
    maxSize=mx;
    currentSize=0;
    HeadArry=new T[maxSize];
}

template<class T>
MaxHeap<T>::~MaxHeap()
{
    delete [] HeadArry;
}

template<class T>
bool MaxHeap<T>::isEmpty()  //檢視是否為空
{
    return currentSize==0;   //為空返1
}

template<class T>
void MaxHeap<T>::Push(const T&e)   //插入資料
{
    if(currentSize==maxSize)
        throw "Head has already full";
    HeadArry[currentSize]=e;   //在最後插入
    trickUp(currentSize++);  //向上滲透
}

template<class T>
void MaxHeap<T>::trickUp(int index) //向上滲透
{
    int parent=(index-1)/2;     //獲取父節點的序號
    T bottom=HeadArry[index];  //先儲存最後一個數
    while((index>0)  && (HeadArry[parent]<bottom)) //當達到根或者子節點比父節點大停止
    {
        HeadArry[index]=HeadArry[parent];  //交換
        index=parent;
        parent=(parent-1)/2;
    }
    HeadArry[index]=bottom;         //找到合適的位置
}

template<class T>
const T& MaxHeap<T>::Top() const       //檢視最頂端的數
{
    return HeadArry[0];
}

template<class T>
void MaxHeap<T>::Pop()      //刪除
{
    HeadArry[0]=HeadArry[--currentSize];  //把最後一個數拿到最上面來
    trickDown(0);  //從根開始滲透
}

template<class T>
void MaxHeap<T>::trickDown(int index)
{
    int large;
    T top=HeadArry[index];  //臨時儲存父節點

    while(index<currentSize/2)  //迴圈到第二層
    {
        int Left=2*index+1;   //左兒子
        int Right=Left+1;     //右兒子
        if(Right<currentSize  && HeadArry[Left]<HeadArry[Right]) //右兒子存在並且右兒子比兒子大
        {
            large=Right;          //得到右兒子
        }
        else
            large=Left;           //得到左兒子
        if(top>= HeadArry[large])  //父節點比兒子大
          break;     //結束
        HeadArry[index]=HeadArry[large];  //否則交換順序
        index=large;       //繼續向下滲透
    }
    HeadArry[index]=top;      //把top插入到這個裡面

}









#endif // __MAXHEAP_H__
#include <iostream>
#include "MaxHeap.h"
using namespace std;

int main()
{
    MaxHeap<int> p;
    p.Push(10);
    p.Push(30);
    p.Push(15);

    //cout<<p.Top()<<endl;
    p.Push(60);
       cout<<p.Top()<<endl;

    p.Pop();
     cout<<p.Top()<<endl;
    //cout << "Hello world!" << endl;
    return 0;
}