堆其实就是由完全二叉树组成的,
目录
堆(heap)
堆(英语:heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。
堆的性质:
1.堆中某个节点的值总是不大于或不小于其父节点的值。
2.堆总是一棵完全二叉树。
堆的分类
将根节点最大的堆叫做最大堆或大顶堆,根节点最小的堆叫做最小堆或小顶堆。常见的堆有二叉堆、斐波那契堆等。
堆的定义如下:n个元素的序列{k1,k2,ki,…,kn}当且仅当满足下关系时,称之为堆。
(ki <= k2i,ki <= k2i+1)或者(ki >= k2i,ki >= k2i+1), (i = 1,2,3,4…n/2)
若将和此次序列对应的一维数组(即以一维数组作此序列的存储结构)看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有非终端结点的值均不大于(或不小于)其左、右孩子结点的值。由此,若序列{k1,k2,…,kn}是堆,则堆顶元素(或完全二叉树的根)必为序列中n个元素的最小值(或最大值)。
每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:
最大堆的插入与删除
由于堆是由完全二叉树构成,所以堆用二叉树的顺序结构组成
由于堆存储在下标从
0
开始计数的一维数组中,因此在堆中给定下标为
i
的结点时
如果
i
= 0
,结点
i
是根结点,无双亲;否则结点
i
的
父结点
为结点 (i-1)/2(向下取整)
;
如果
2
i
+1
>
n
-
1
,则结点
i
无左子女;否则结点
i
的
左子女
为结点
2
i
+1
;
如果
2
i
+2
>
n
-
1
,则结点
i
无右子女;否则结点
i
的
右子女
为结点
2
i
+2
。
最小堆的定义
template <class T, class E>
class MinHeap : public MinPQ<T, E> {
//最小堆继承了(最小)优先级队列
public:
MinHeap (int sz = DefaultSize); //构造函数
MinHeap (E arr[], int n); //构造函数
~MinHeap() { delete [ ] heap; } //析构函数
bool Insert (E& d); //插入
bool Remove (E& d); //删除
bool IsEmpty () const //判堆空否
{ return currentSize == 0; }
bool IsFull () const //判堆满否
{ return currentSize == maxHeapSize; }
void MakeEmpty () { currentSize = 0; } //置空堆
private:
E *heap; //最小堆元素存储数组
int currentSize; //最小堆当前元素个数
int maxHeapSize; //最小堆最大容量
void siftDown (int start, int m); //调整算法
void siftUp (int start); //调整算法
};
最小堆的建立
template <class T, class E>
MinHeap<T>::MinHeap (int sz) {
maxHeapSize = (DefaultSize < sz) ? sz : DefaultSize;
heap = new E[maxHeapSize]; //创建堆空间
if (heap == NULL) {
cerr << “堆存储分配失败!” << endl; exit(1);
}
currentSize = 0; //建立当前大小
};
初始化最小堆(向下调整)
template <class T, class E>
MinHeap<T>::MinHeap (E arr[], int n) {
maxHeapSize = (DefaultSize < n) ? n : DefaultSize;
heap = new E[maxHeapSize];
if (heap == NULL) {
cerr << “堆存储分配失败!” << endl; exit(1);
}
for (int i = 0; i < n; i++) heap[i] = arr[i];
currentSize = n; //复制堆数组, 建立当前大小
int currentPos = (currentSize-2)/2;//减去n包含的0节点与最后一位n节点
//找最初调整位置:最后分支结点
while (currentPos >= 0) { //逐步向上扩大堆
siftDown (currentPos, currentSize-1);
//局部自上向下下滑调整
currentPos--;
}
};
template <class T, class E>
void MinHeap<T>::siftDown (int start, int m ) {
//私有函数: 从结点start开始到m为止, 自上向下比较,
//如果子女的值小于父结点的值, 则关键码小的上浮,
//继续向下层比较, 将一个集合局部调整为最小堆。
int i = start, j = 2*i+1; //j是i的左子女位置
E temp = heap[i];
while (j <= m) { //检查是否到最后位置
if ( j < m && heap[j] > heap[j+1] ) j++;
//让j指向两子女中的小者
if ( temp <= heap[j] ) break; //小则不做调整
else { heap[i] = heap[j]; i = j; j = 2*j+1; }
//否则小者上移, i, j下降
}
heap[i] = temp; //回放temp中暂存的元素
};
最小堆的插入(向上调整)
每次插入都加在堆的最后,再自下向上执行调整,使之重新形成堆,时间复杂性O(log2n)。
template <class T, class E>
bool MinHeap<T>::Insert (const E& x ) {
//公共函数: 将x插入到最小堆中
if ( currentSize == maxHeapSize ) //堆满
{ cerr << "Heap Full" << endl; return false; }
heap[currentSize] = x; //插入
siftUp (currentSize); //向上调整
currentSize++; //堆计数加1
return true;
};
template <class T, class E>
void MinHeap<T>::FilterUp (int start) {
//私有函数: 从结点start开始到结点0为止, 自下向上
//比较, 如果子女的值小于父结点的值, 则相互交换,
//这样将集合重新调整为最小堆。关键码比较符<=
//在E中定义。
int j = start, i = (j-1)/2; E temp = heap[j];
while (j > 0) { //沿父结点路径向上直达根
if (heap[i] <= temp)
break; //父结点值小, 不调整
else {
heap[j] = heap[i];
j = i;
i = (i-1)/2; }
//父结点结点值大, 调整
}
heap[j] = temp; //回送
};
最小堆的删除算法(向下调整)
template <class T, class E>
bool MinHeap<T>::Remove (E& x) {
if ( !currentSize ) { //堆空, 返回false
cout << "Heap empty" << endl; return false;
}
x = heap[0];
heap[0] = heap[currentSize-1];
currentSize--;
siftDown(0, currentSize-1); //自上向下调整为堆
return true; //返回最小元素
};