1 基本概念
- 堆是一颗完全二叉树
- 完全二叉树:
- 大顶堆:父节点的值总是大于子节点
- 小顶堆:父节点的值总是小于子节点
2 相关操作
从小顶堆来描述
2.1 插入操作:
步骤:
- (1) 在树的叶子节点的一个空闲位置创建一个空穴,把插入的X放入空穴:【对应到数组操作就是在末尾添加元素】
- (2) 如果此时堆结构不被破坏(X大于等于父节点),插入完成。否则进行步骤(3)
- (3) 与父节点交换。返回到步骤(2)。
2.2 删除操作:
一般都是删除堆顶的元素
步骤:
- (1) : 把根元素移除,形成空穴,把最后的元素,也就是值最大的元素放置在空穴的位置。【对应到数组的操作就是,把数组最后一位覆盖第一位的值,数组长度-1】
- (2) : 判断此时堆结构有无破坏。(父节点是否小于子节点)如果没有,删除结束,否则进行第三步。
- (3): 把此时的父节点与最小的子节点进行交换,转到第二步。
2.3 源程序
#include <vector>
#include <iostream>
using namespace std;
template<typename T>
class cmp{
public:
bool operator()(T a, T b){
return a > b;
}
};
//小顶堆
template<typename T, class F>
class heap{
public:
std::vector<T> h;
F f;
public:
heap():numOfHeap(0){}
~heap() = default;
//插入元素
void insert(T num);
//删除根节点元素,并返回根节点的值
T deleteNode();
private:
//调整位置为pos的元素,up代表向上调整,返回调整后的位置
int adjest(int pos, bool up);
//
int numOfHeap;
};
template<typename T, class F>
void heap<T, F>::insert(T num){
//首先添加到尾部
h.push_back(num);
++numOfHeap;
int n = numOfHeap - 1;
//如果n位置的父节点比他还大,就说明需要调整
while(f(num, h[(n-1)/2])){
n = adjest(n, true);
}
}
template<typename T, class F>
int heap<T, F>::adjest(int pos, bool up){
//上行操作
if(up){
//交换父节点和本节点
T temp = h[(pos-1)/2];
h[(pos-1)/2] = h[pos];
h[pos] = temp;
//返回交换之后的节点位置
return (pos-1)/2;
} else {
//交换较小的子节点
if(pos * 2 + 2 < numOfHeap && f(h[pos * 2 + 2], h[pos * 2 + 1])){
//交换子节点和本节点
T temp = h[pos * 2 + 2];
h[pos * 2 + 2] = h[pos];
h[pos] = temp;
//返回交换之后的节点位置
return pos * 2 + 2;
} else {
//交换子节点和本节点
T temp = h[pos * 2 + 1];
h[pos * 2 + 1] = h[pos];
h[pos] = temp;
//返回交换之后的节点位置
return pos * 2 + 1;
}
}
}
template<typename T, class F>
T heap<T, F>::deleteNode(){
if(h.empty()){
return false;
}
T res = h[0];
h[0] = h[numOfHeap - 1];
h.pop_back();
--numOfHeap;
int n = 0;
while((n*2 + 1 < numOfHeap && f(h[n *2 +1], h[n])) || (n*2 + 2 < numOfHeap && f(h[n *2 + 2], h[n]))){
n = adjest(n, false);
}
return res;
}
int main(int argc, char *argv[]){
heap<int, cmp<int>> h0;
for(int i = 12; i != 0; --i)
h0.insert(i);
while(!h0.h.empty()){
cout << h0.deleteNode() << " ";
}
cout << endl;
}