最小堆
假设某一集合将所有元素按完全二叉树的顺序结构存放在一维数组中,并且在二叉树中每个节点都小于或等于其子节点,则称这个集合为最小堆。
若用数组来存储一个完全二叉树,某个节点在数组的下标为i:
- 若i=0,节点i是根节点,且无父节点,否则i的父节点为节点[(i-1)/2];
- 如果2i+1>n-1,则节点i无左子节点,否则节点i的左子节点为节点[2i+1];
- 如果2i+2>n-1,则节点i无右子节点,否则节点i的右子节点为节点[2i+2]。
最小堆的类定义
template<class T>
class MinHeap
{
public:
MinHeap(int sz = 10); //创建大小为sz的堆
MinHeap(T arr[], int n); //通过数组建立堆
~MinHeap() { delete[] heap; } //析构函数
bool insert(const T& x); //将元素插入到最小堆中
bool removeMin(T& x); //删除堆中最小元素 即为堆顶元素
bool getMin(T& x) { if (isEmpty()) return false; x = heap[0]; return true; } //取得堆顶元素
bool isEmpty() { return currentSize == 0; } //判断堆是否为空
bool isFull() { return currentSize == maxHeapSize; } //判断堆是否为满
void makeEmpty() { currentSize = 0; } //置空堆
private:
T* heap; //存放最小堆元素的数组
int currentSize; //最小堆中元素个数
int maxHeapSize; //最小堆中最多允许元素个数
void siftDown(int start); //元素从start下滑调整到合适位置
void siftUp(int start); //从start上滑调整到合适位置
};
向上和向下调整函数
向上和向下调整函数是类中的私有函数,主要功能是将堆中的某个元素向上或向下调整使其放置到合适的位置。
向上调整函数
向上调整函数是从某个子节点开始,沿其父节点寻找,直至找到父节点比其小的位置或者达到根节点,否则就一直将其与其父节点交换位置。
template<class T>
void MinHeap<T>::siftUp(int start)
{
T temp = heap[start];
int j = start, i = (j - 1) / 2;
while (j > 0) //沿父节点寻找直到根节点
{
if (heap[i] <= temp) break; //若找到位置则退出循环
heap[j] = heap[i]; //将大的元素下移
j = i; //重复操作直至找到位置或者达到根节点
i = (j - 1) / 2;
}
heap[j] = temp;
}
向下调整函数
向下调整函数与向上调整函数类似,从某个节点开始向下调整,将其子节点中较小的元素与其交换位置,直至子节点都比其小或者无子节点。
template<class T>
void MinHeap<T>::siftDown(int start)
{
int i = start, j = 2 * i + 1;
T temp = heap[i];
while (j <= currentSize - 1)
{
if (j < currentSize - 1 && heap[j + 1] < heap[j]) j++; //寻找左右节点中较小的元素
if (temp <= heap[j]) break; //若找到位置 则退出循环
heap[i] = heap[j]; //将较小的元素上移
i = j; //继续重复下移操作直至找到位置或者到达末尾
j = 2 * i + 1;
}
heap[i] = temp;
}
构造函数
-
创建一个指定大小的空堆
MinHeap<T>::MinHeap(int sz) : maxHeapSize(sz) { heap = new T[maxHeapSize]; currentSize = 0; }
-
通过一个数组里的元素创建堆
通过数组创建堆有两种方法,第一种是创建一个空堆以后将数组中的每个元素直接加入到堆中;第二种是直接将数组中的每个元素放入堆数组中,接着再从下往上对每个元素进行向下调整使其成为一个最小堆。下面给出的是第二种方法的代码:
template<class T> MinHeap<T>::MinHeap(T arr[], int n) : maxHeapSize(n) { heap = new T[maxHeapSize]; for (int i = 0; i < maxHeapSize; i++) heap[i] = arr[i]; currentSize = maxSize; //从最后一个分支点开始向前逐个向下调整 int curPos = (currentSize - 1) / 2; //最后一个分支点的位置 while (curPos >= 0) //一直往前走 { siftDown(curPos--); //向下调整 } }
往堆中插入元素
往队中插入元素的步骤是先判断堆是否已满,若未满则直接在数组尾插入该元素,即在完全二叉树中新加元素使其仍然是一棵完全二叉树,再向上调整这个尾插的元素,使其仍然是一个最小堆。
template<class T>
bool MinHeap<T>::insert(const T& x)
{
if (isFull()) return false;
heap[currentSize] = x;
siftUp(currentSize++); //向上调整
return true;
}
删除堆中最小元素
删除堆中最小元素,即是删除堆顶元素,做法是将堆中最后一个元素覆盖原堆顶元素,接着再从堆顶向下调整,使其调整到合适的位置。
template<class T>
bool MinHeap<T>::removeMin(T& x)
{
if (isEmpty()) return false;
x = heap[0];
//将最后一个元素到堆顶位置后下移调整
heap[0] = heap[--currentSize];
siftDown(0);
return true;
}