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