声明:博客主要知识点内容来自参考书《数据结构、算法与应用》,本篇博客的主要目的是记录学习过程,方便日后复习。
堆的定义:一棵大根树(小根树)是这样一棵树,其中每个节点的值都大于(小于)或等于其子节点(若有子节点的话)的值。一个大根堆(小根堆)既是大根树(小根树),也是完全二叉树。
堆的常见操作:
- 大根堆的插入操作:插入过程基本是这样的,将新的元素插入新的节点,然后沿着新的节点到根节点的路径,执行一趟起泡操作(基本就是两两比较的操作,若比父节点大就执行交换操作),将新元素与父节点的元素进行比较交换,直到后者大于或者等于前者为止。在插入函数push的代码中,首先判断数组heap是否有空间容纳新的元素,如果没有的话需要扩容。然后令变量currentNode指向新的叶子节点的位置。接下来,沿着叶子节点向根节点的路径进行遍历。实际上,是将新元素theElement沿着这条路径起泡上浮,直至到达合适的位置。在每一次确定上移位置currentNode的值时,我们需要判断,是否到达根节点(也即currentNode == 1),或者是否在currentNode位置插入新的元素之后符合大根树的特性(也即theElement <=heap[currentNode/2]).。如果满足一个条件,就将新元素theElement插入的位置currentNode。否则,进入while循环体,将位置currentNode/2位置的元素下移至currentNode,然后令,currentNode = currentNode/2。
- 大根堆的插入C++示例代码:
template<class T>
void maxHeap<T>::push(const T& theElement)
{
//增加数组长度
if(heapSize == arrayLength -1)
{
changeLength1D(heap,arrayLength,2*arrayLength);
arrayLength = 2*arrayLength;
}
//为新的元素theElement找到插入位置
int currentNode = ++heapSize;
while(currentNode !=1 && heap[currentNode/2] <theElement)
{
//父节点下移
heap[currentNode] = heap[currentNode/2];
currentNode = currentNode/2; //currentNode移向双亲
}
heap[currentNode] = theElement;
}
- 大根堆的删除操作:在大根堆中删除一个元素,就是删除根节点的元素。删除根节点之后,需要进行大根堆的重构。以下是C++操作示例代码:pop操作要删除的是堆中的最大元素,它存在根heap[1]中。删除的过程是,把堆的最后一个元素heap[heapSize]保存到变量lastElement中,然后堆的元素个数heapSize减一。在while循环中,我们把heap数组重建为大根堆。重建堆的过程是为存储在lastElement中元素寻找合适的插入位置,寻找过程从根开始沿堆向下进行。
-
template<class T> void maxHeap<T>::pop() { if(heapSize == 0) //堆为空 throw queueEmpty(); //堆不为空的话,删除第一个元素 heap[1].~T(); //删除最后一个元素,然后重建堆 T lastElement = heap[heapSize--]; //从根节点开始,为最后一个元素寻找位置 int currentNode = 1; int child = 2; //currentNode的孩子 while(child<heapSize) { //heap[child]应该的currentNode的更大的孩子 if(child<heapSize && heap[child]<heap[child+1]) child++; //如果lastElement比heap[child]还大,就可以将lastElement放在currentNode位置上 if(lastElement >heap[child]) break; //否则的话,将孩子向上移动,currentNode向下一层寻找位置 heap[currentNode] = heap[child]; currentNode = child; child = child*2; } heap[currentNode] = lastElement; }
初始化一个非空大根堆:方法initialize在数组heap中建堆。函数一开始,令heap指向数组theHeap,heapSize等于数组中元素个数theSize。在for循环中,我们从最后一个具有孩子的节点到根节点进行扫描,并用root表示正在处理的节点。对于每一个root值,嵌入的while循环把以root为根的子树调整为大根堆。
template<class T>
void maxHeap<T>::initialize(T *theHeap,int theSize)
{
//在数组theHeap[1:theSize]中建大根堆
delete [] heap;
heap = theHeap;
heapSize = theSize;
//堆化
for(int root = heapSize/2;root>=1;root--)
{
T rootElement = heap[root];
//为元素rootElement寻找位置
int child = 2*root;
while(child < heapSize)
{
//heap[child]应该是兄弟中的较大者
if(child<heapSize && heap[child]< heap[child+1])
child++;
//可以将元素rootElement放在heap[child/2]嘛?
if(rootElement >= heap[child])
break; //可以
//不可以
heap[child/2] = heap[child]; //把孩子向上移
child *=2;
}
heap[child/2] = rootElement;
}
}