小根堆的性质:是完全二叉树,根节点的关键字小于等于两个子节点的关键字。
插入和删除操作都应当保证小根堆的性质不变。
由于小根堆是完全二叉树,所以使用数组存放具有显著优势:按照层次遍历将具有n个元素的小根堆的存放到数组(命名为heap)的[1]~[n]项中。此时对于任一元素heap[i],其父节点为heap[i/2],其子节点为heap[i * 2] 和 heap[i*2+1]。
1、插入元素:从新的叶节点开始,向上考察其父节点,若父节点的关键字大于新节点的关键字,则二者交换位置,然后继续向上考察;否则循环停止,插入位置确定。
2、删除根节点(删除最小节点):首先删除heap[1],然后考察最后一个叶节点能否填入空位(保证填入后依然是小根堆),如果能,则将该叶节点填入空位;如果不能,则将空位的两个子节点中较小的一个填入空位,然后对新的空位进行同样的的操作。
3、将数组堆化:从最后一个有子节点的节点(由完全二叉树的性质可知,该节点在数组中的下标是节点个数长度/2)开始向前考察,对于考察到的每个节点,比较该节点的关键字与其较小子节点的关键字,若该节点的关键字更大,则二者交换位置,然后继续比较该节点的关键字与新位置上较小子节点的关键字,直到该节点位置确定。
代码
template<class T>
class minTree//小根堆类
{
public:
minTree(int initlength=100)
{
arrayLength = initlength;
heap = new T[arrayLength];
heap[0] = NULL;
heapSize = 0;
}
minTree(T* theHeap, int theSize,int theLength)//利用已知数组创建小根堆
{
heap = theHeap;
heapSize = theSize;
arrayLength = theLength;
//将传入的数组堆化
for (int root = heapSize / 2; root >= 1; root--)//heap[heapSize/2]是堆中最后一个有子节点的节点,从该节点开始向前遍历
{
T rootElement = heap[root];
int child = root * 2;//左子节点的下标
while (child <= heapSize)
{
if (child<heapSize && heap[child]>heap[child + 1])//从左子节点和右子节点中找出较小的
child++;
if (rootElement <= heap[child])//若根节点比两个子节点都小,则不再需要交换,又因为子节点以下都已经排好顺序,所以直接退出循环
break;
heap[child / 2] = heap[child];//若根节点大于其中一个子节点,则交换,然后在交换完之后的位置上继续向下考察
child *= 2;
}
heap[child / 2] = rootElement;
}
}
T front()//获取堆中首项,即最小值
{
return heap[1];
}
bool empty()
{
return heapSize == 0;
}
void push(const T& theElement)//插入元素
{
if (heapSize == arrayLength - 1)//数组若满长度加倍
{
T* newHeap = new T[arrayLength * 2];
for (int i = 0; i < arrayLength; i++)
newHeap[i] = heap[i];
delete[]heap;
heap = newHeap;
arrayLength *= 2;
}
heapSize++;
int currentNode = heapSize;//新节点是最后一个节点
while (currentNode != 1 && heap[currentNode / 2] > theElement)//从新节点开始向上考察其父节点,若父节点比当前节点大,则交换位置
{
heap[currentNode] = heap[currentNode / 2];
currentNode /= 2;
}
heap[currentNode] = theElement;
}
void pop()//删除最小元素,即删除heap[1]
{
if (heapSize == 0)//当前堆为空,则直接退出
{
cout << "空堆" << endl;
return;
}
heap[1].~T();
//重建小根堆,由于需要保持完全二叉树的性质,所以在尽可能调用堆的最后一个节点来补足空位
T lastElement = heap[heapSize];
heapSize--;
int currentNode = 1;//此时该位为空,需要由最后一个节点或子节点来补足
int child = 2;
while (child <= heapSize)
{
if (child<heapSize && heap[child]>heap[child + 1])//选择子节点中较小的一个
child++;
if (lastElement <= heap[child])//若最后一个结点比子节点小,则结束循环,由最后一个节点来补足空位
break;
heap[currentNode] = heap[child];//否则由子节点来补足空位,然后继续向下考察
currentNode = child;
child *= 2;
}
heap[currentNode] = lastElement;
}
private:
T* heap;//堆中元素存放在数组中
int arrayLength;//数组容量
int heapSize;//堆中元素个数
};