数据结构-----最大堆的实现
定義:一棵大根樹(小根樹)是這樣一棵樹,其中每一個節(jié)點的值都大小(小于)或等于其子節(jié)點(如果有子節(jié)點的話)的值。
一個大根堆(小根堆)既是大根樹(小根樹)也是完全二叉樹。
大根堆
小根堆
本篇主要實現(xiàn)大根堆的初始化,插入以及刪除操作。
在實現(xiàn)這些之前,先來簡單介紹一下大根堆類的主要數(shù)據(jù)成員:
私有成員變量:
heap:一維數(shù)組,用于存儲堆中的元素;
heapSize:整型變量,用于記錄堆中元素個數(shù);
arrayLength:整形變量,用于記錄堆最大容量,即最多可以容納多少個元素;
公有成員函數(shù):
initialize(T* ,int):初始化最大堆,第一個形參為初始化的數(shù)組,第二個形參為元素個數(shù);
remove(int);刪除最大堆中特定元素;
push(const T&);添加新元素;
私有成員函數(shù):
changeArrayLength(T*, int):當實際容量等于額定容量時,擴充額定容量;
adjust(T&,int, int):用于調(diào)整堆結(jié)構(gòu)的函數(shù),插入刪除和初始化都會用到;
堆雖然也是二叉樹,但本例中沒有使用構(gòu)建樹的方式(即用節(jié)點指針)來構(gòu)建最大堆,而是用數(shù)組的方式存儲元素,這樣我們假設(shè)數(shù)組[1:heapSize]用來存儲堆元素,而對堆中元素進行調(diào)整的過程就是改變元素在數(shù)組中位置的過程。
另外需要明確的是,1是根節(jié)點的下標,heapSize/2是最后一個節(jié)點的父節(jié)點下標。
對于某一個節(jié)點下標n來說,如果2*n<=heapSize,則2*n是該節(jié)點左孩子的下標;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?如果2*n+1<=heapSize,則2*n+1是該節(jié)點右孩子的下標。
首先來看一下初始化操作的實現(xiàn),傳入的參數(shù)為一維數(shù)組和元素個數(shù)。
步驟1:用參數(shù)的數(shù)組初始化堆中數(shù)組heap,用第二個參數(shù)初始化堆中元素個數(shù)heapSize;
步驟2:從最后一個節(jié)點的父節(jié)點開始(heapSize/2),一個個循環(huán),每次循環(huán)結(jié)束節(jié)點索引減一。即第一次是heapSize/2,第二次則是heapSize/2-1,第三次是heapSize/2-2,一直循環(huán)到1。暫且叫步驟2為外層循環(huán),該循環(huán)是逐個向上。每循環(huán)完一次都確保以該下標的節(jié)點為根節(jié)點的子樹是一個最大堆。
步驟3:在步驟2中的每一次循環(huán)中(內(nèi)層循環(huán)),逐層向下索引,同時比較當前結(jié)點和其子節(jié)點的大小,將大節(jié)點作為子樹的根節(jié)點,將原先的根節(jié)點下移到子樹的位置。直到索引到達heapSize。與步驟2不同,步驟3是逐層向下索引,即某一次索引為n,則下一次是2*n。
template<class T> void maxHeap<T>::initialize(T* theHeap, int theSize) {delete[] heap; //刪除堆中原先的元素heap = theHeap; //指針賦值,將參數(shù)中數(shù)組的元素賦值給堆heapSize = theSize; //元素個數(shù)賦值//執(zhí)行循環(huán),從最后一個節(jié)點的父節(jié)點開始,逐個向上,外層循環(huán)for(int root = heapSize/2; root >= 1; --root) {T theElement = heap[root]; //保存外層循環(huán)的當前結(jié)點,每次都可以理解為給這個值找位置。//內(nèi)層循環(huán)開始,給theElement找位置,將大的上移,大節(jié)點原先的位置變成空位置。int currentNode = root; //始終是空位置的下標,也是child下標的父節(jié)點位置int child = 2*root; //當前結(jié)點左孩子下標while(child <= heapSize){if(child < heapSize && heap[child+1] > heap[child])++child; //令child指向值較大的孩子//如果theElement比兩個孩子都大,那么就證明找到位置if(theElement > heap[child])break;//如果沒有找到,將大的節(jié)點放在空位置上,大節(jié)點原先的位置變?yōu)榭瘴恢胔eap[currentNode] = heap[child];current = child;//更新孩子下標,繼續(xù)向下尋找位置child *= 2; }}//找到位置,將theElement放在空位置上heap[currentNode] = theElement; }接下來是插入操作,參數(shù)是要插入的值theElement,原理同初始化一樣,也是為theElement找位置,只是開始的位置不是heapSize/2,而是(++heapSize)/2。此時空位置是heapSize。
步驟1:檢查數(shù)組容量,如果數(shù)組滿了,則需要擴充數(shù)組大小。
步驟2:堆元素個數(shù)加一。
步驟3:比較theElement和當前節(jié)點大小,如果theElement大,則把當前結(jié)點移動到空位置上,把該節(jié)點原先的位置設(shè)為空位置。繼續(xù)向上尋找。
template<class T> void maxHeap<T>::push(const T& theElement) {//檢測堆是否已滿,若滿則需要擴充if(heapSize == arrayLength - 1){changeArrayLength(heap, 2*heapSize);heapSize *= 2;}int currentNode = ++heapSize; //堆元素個數(shù)加一,空位置為末尾下標//從下到上一層一層比較,把小節(jié)點下移,空位置上移。//直到空位置的父節(jié)點比theElement大,子節(jié)點比theElement小while(currentNode != 1 && heap[currentNode/2] < theElement){heap[currentNode] = heap[currentNode/2];currentNode /= 2;}heap[currentNode] = theElement; }最后是刪除操作,參數(shù)是要刪除的下標索引。刪除后,以該位置為根節(jié)點的子樹已經(jīng)不是最大堆,只需要對這一小部分進行調(diào)整。
原理依舊相同,把要刪除的那個下標設(shè)為空位置。逐層向下尋找位置,把大節(jié)點上移,空位值下移。
步驟1:取出末尾下標的元素theElement,并將堆元素個數(shù)減一。
步驟2:在以刪除節(jié)點為根節(jié)點的子樹中為theElement找位置。
template<class T> T maxHeap<T>::remove(int index) {//判斷下標是否合法if(index < 1 || index > heapSize)throw heapError("Error: the index is illegal"); T theElement = heap[heapSize--]; //取出最后一個元素,同時元素個數(shù)減一int currentNode = index; //始終指向空位置,也是child的父節(jié)點int child = 2 * index; while(child <= heapSize){//令child是較大孩子的下標if(child < heapSize && heap[child+1] > heap[child])child++;//如果theElement大,則說明找到位置存放theElement了if(theElement > heap[child])return;//若沒找到,繼續(xù)向下尋找heap[currentNode] = heap[child];currentNode = child;child *= 2;}heap[currentNode] = theElement;return theElement; }
總結(jié)
以上是生活随笔為你收集整理的数据结构-----最大堆的实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构-----红黑树的插入操作
- 下一篇: Qt学习笔记-----信号槽