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;
}