堆
(二叉)堆是一种使用数组实现完全二叉树的数据结构,树上的每一点对应数组的一个元素。堆经常被用来实现优先队列和堆排序。下面为堆的示意图。
如图,下面为计算各亲属下标的公式。公式中r表示结点的下标。它的范围在0到n - 1之间。n表示二叉树结点的总数。
parent(r) = (r - 1) / 2 当 r ≠ 0 时
leftChild(r) = 2r + 1 当 2r + 1 < n 时
rightChild(r) = 2r + 2 当 2r + 2 < n 时
二叉堆可以分为两种形式:最大堆和最小堆。
在最大堆中,除了根结点以外的的所有结点 i 都需要满足: A[ parent(i) ] >= A[ i ] ,也就是说,某个结点的值至多与其父结点一样大。因此,堆中最大的元素存放在根结点;并且在任一子树中,该子树包含的的所有结点的值都不大于子树该根结点的值。
最小堆的性质则与最大堆刚好相反: A[ parent(i) ] <= A [ i ] ,最小堆把最小的元素值存放在根结点。
优先队列
一些按照重要性或优先级来组织的对象称为优先队列。我们可以用最大堆来实现优先队列,出队即把最大堆的根结点删除并返回值,然后继续维护最大堆的性质。在最大堆中,分支结点的下标为0 ~ number / 2 - 1 , 叶结点的下标为 number / 2 ~ number - 1。
下面为实现最大堆的代码:
template <class Elem> class maxheap
{
private:
Elem* Heap; // 存放元素的堆(数组)
int size; // 堆的大小
int number; // 堆目前存放的元素个数
void heapify(int); // 维持最大堆的性质,输入的值必须保证左右孩子都是最大堆
void swap(Elem A[], int i, int j) // 交换数组中的两个值
{
Elem temp = A[i]; A[i] = A[j]; A[j] = temp;
}
public:
maxheap(Elem* h, int s, int n)
: Heap(h), size(s), number(n) { buildHeap(); } // 初始化成员并建堆
~maxheap() {}
// 判断是否叶结点
bool isLeaf(int pos) const { return (pos >= number / 2) && (pos < number); }
// 返回堆的元素个数
int heapSize() const { return number; }
// 返回输入结点下标的左孩子的下标
int leftChild(int pos) const { return (pos << 1) + 1; }
// 返回输入结点下标的右孩子的下标
int rightChild(int pos) const { return (pos << 1) + 2; }
// 返回输入结点下标的父结点下标
int parent(int pos) const { return (pos - 1) >> 1; }
// 向堆中插入元素,成功返回true
bool insert(const Elem&);
// 删除堆中值最大的元素,即根结点,并返回其值,成功返回true
bool removeMax(Elem&);
// 提高指定位置结点元素的值(修改优先级)
bool increasePri(int,const Elem&);
// 建堆
void buildHeap()
{
for (int i = number / 2 - 1; i >= 0; --i) //只对分支结点执行
heapify(i); // 分别维持最大堆的性质
}
};
// 维持最大堆的性质,输入时要求保证其左右结点也是最大堆
template <class Elem>
void maxheap<Elem>::heapify(int pos)
{
while (!isLeaf(pos)) // 只对分支结点执行,用来中止向下传递到叶结点的情况
{
int left = leftChild(pos); // 取左孩子下标, 并用来存放左右孩子中的最大值
int right = rightChild(pos); // 取右孩子下标
if ((right < number) && (Heap[left] < Heap[right])) // 若存在右孩子且大于左孩子
left = right;
if ((Heap[pos] > Heap[left])) return; // 若该结点大于其孩子结点值,直接退出函数
swap(Heap, pos, left); // 把最大元素值与原来值交换
pos = left; // 向下延伸
}
}
// 向堆中插入元素,成功返回true
template <class Elem>
bool maxheap<Elem>::insert(const Elem& elem)
{
if (number >= size) return false;
int current = number++; // 堆元素个数加一,并用存起原来的副本
Heap[current] = elem; // 把元素插到数组末尾,即为叶结点
// 新插入的结点与父结点比较,当current为0时跳过,因为是根结点
while ((current != 0) && (Heap[current] > Heap[parent(current)]))
{
swap(Heap, current, parent(current));
current = parent(current); // 向上攀爬
}
return true;
}
// 删除堆中值最大的元素,即根结点,并返回其值,成功返回true
template <class Elem>
bool maxheap<Elem>::removeMax(Elem& elem)
{
if (number == 0) return false;
swap(Heap, 0, --number); // 用根结点的值与最后一个结点的值交换,并把长度减一,相当于删除根结点
if (number != 0) // 如果还有元素
heapify(0);
elem = Heap[number];
return true;
}
// 提高指定位置结点元素的值(修改优先级)
template <class Elem>
bool maxheap<Elem>::increasePri(int pos, const Elem& elem)
{
if (Heap[pos] > elem) return false; // 原来的优先级比现在低
Heap[pos] = elem; // 提高优先级
while ((pos != 0) && (Heap[pos] > Heap[parent(pos)]))
{
swap(Heap, pos, parent(pos));
pos = parent(pos); //向上攀爬
}
return true;
}