堆的定义及其实现
最小堆:最小堆是一个关键码序列{ K0,K1,...Kn-1},它具有如下特性:
- Ki ≤K2i+1
- Ki≤K2i十2
- 类似可以定义最大堆
堆的性质
- 可以看出是完全二叉树
- 堆中的数据是局部有序的,堆不唯一
- 兄弟节点之间没有限制
- 从逻辑结构来看堆实际上是树形结构
最小堆的类定义(ADT)
template <class T> class MinHeap {
private:
T* heapArray;
int CurrentSize;
int MaxSize;
void BuildHeap();
public:
MinHeap(const int n); //构造函数,n为最大元素数目
virtual ~MinHeap(){delete []heapArray;}; // 析构函数
bool isLeaf(int pos) const;
int leftchild(int pos) const;
int rightchild(int pos) const;
int parent(int pos) const;
bool Remove(int pos, T& node);
bool Insert(const T& newNode);
T& RemoveMin();// 从堆顶删除最小值
void SiftUp(int position); // 从position向上开始调整,使序列成为堆
void SiftDown(int left);// 筛选法函数,参数left表示开始处理的数组下标
}
复制代码
堆最小堆使用SiftDown和SiftUp
template<class T>
void MinHeap<T>::SiftDown(int position){
int i = position;
int j = 2*i+1;//先指向左子节点,但最终存储的是最小子节点
Temp = headArray[i];
while(j<CurrentSize){
if(j<CurrentSize-1 && headArray[j]>headArray[j+1])
j++; //最小节点
if(Temp > headArray[j])
{
headArray[i] = headArray[j]; //注意此处j的值不变,以用来其他的值替换
i=j;
j=2*i+1; //继续下降 
}
else break;
}
headArray[j] = Temp;
}
template<class T>
void MinHeap<T>::SiftUp(int position) {
// 从position向上开始调整,使序列成为堆
int temppos=position;
// 不是父子结点直接swap
T temp=heapArray[temppos];
while((temppos>0) && (heapArray[parent(temppos)] > temp)) {
heapArray[temppos]=heapArray[parent(temppos)]; temppos=parent(temppos);
}
heapArray[temppos]=temp;// 找到最终位置
}
复制代码
建最小堆过程
首先,将 n 个关键码放到一维数组中
- 整体不是最小堆
- 所有叶结点子树本身是堆
- 当i≥[n/2]时,以关键码 Ki 为根的子树已经是堆
- 从倒数第二层,i = [n/2- 1] 开始
- 从右至左依次调整
- 直到整个过程到达树根
- 整棵完全二叉树就成为一个堆
实现上比较简单: 首先初始化堆,然后从第一个分支结点heapArray[CurrentSize/2-1]开始,自底向上逐步把以子树调整成堆
template<class T>
void MinHeap<T>::BuildHeap(){
for(int i=CurrentSize/2 -1;i>=0;i--){
SiftDown(i);
}
}
复制代码
插入:
template <class T>
bool MinHeap<T>::Insert(const T& newNode)
{
//向堆中插入新元素newNode {
if(CurrentSize==MaxSize) return false;
// 堆空间已经满
heapArray[CurrentSize]=newNode;
SiftUp(CurrentSize); // 向上调整
CurrentSize++;
}
复制代码
删除
template<class T>
bool MinHeap<T>::Remove(int pos, T& node) {
if((pos<0)||(pos>=CurrentSize))
return false;
T temp=heapArray[pos];
heapArray[pos]=heapArray[--CurrentSize];
if (heapArray[parent(pos)]> heapArray[pos])
SiftUp(pos); //上升筛
else SiftDown(pos); // 向下筛 node=temp;
return true;
}
复制代码
时间复杂度
建堆效率分析 n个结点的堆,高度d=log2n+1。 根为第 0 层,则第 i 层结点个数为 2i, 考虑一个元素在堆中向下移动的距离。
- 大约一半的结点深度为d-1,不移动(叶)。
- 四分之一的结点深度为d-2,而它们至多能向下移动一层。
- 树中每向上一层,结点的数目为前一层的一半,而子 树高度加一。因而元素移动的最大距离的总数为
堆有logN层深,所以插入删除的平均时间和最差时间都是O(logN)
最大最小堆
能在一个数据结构中同时维护最大值和最小值. 最小最大堆是一棵完全二叉树,且其中每个元素有一个key数据成员。
树的各层交替为最小层和最大层。
根结点在最小层。
设x是最小最大堆的任意结点。若x在最小(最大)层上,则x中的元素的key值在以x为根的子树的所有元素中是最小(最大)的。位于最小(最大)层的结点称为最小(最大)结点。
优先队列(priority_queue)
可以看出,堆可以用于实现优先队列。
- 优先队列
- 根据需要释放具有最小(大)值的对象
- 最大树、 左高树HBLT、WBLT、MaxWBLT
- 改变已存储于优先队列中对象的优先权
- 辅助数据结构帮助找到对象
C++优先队列容器与队列一样,只能从队尾添加(插入)元素,从队头(队首)删除元素。但他有一个特性,就是队列中最大的元素总是位于队首,所以出队时,并非按先进先出的原则进行,而是将当前队列中最大的元素出队。
这点类似与给队列里的元素进行了由大到小的顺序排序。
元素的比较规则默认为按元素的值的由大到小排序;当然,可以重载“<”操作符来重新定义比较规则(也就是最大堆与最小堆)。