以下是视频讲解的配套代码: 戳我看超详细解析~有画图有手撕代码
二叉堆概念
- 堆是一棵完全二叉树 (完全二叉树省略介绍)
- 堆顶是最大/最小 (此处的最大最小是广义的)
- 堆顶的左右儿子也是堆
堆的储存
由于是完全二叉树 所以直接就能用数组来进行储存
private:
vector<T> v;
int size; //堆的大小 总比v大小小1 (v0是垃圾值)
并且使用
#define parent (root>>1)
#define left (root<<1)
#define right (left|1)
来获得某个节点的父亲与左右儿子 (数组下标需要从1开始)
//构造函数写一下
Heap()
{
size = 0;
v.push_back(T());
}
插入
- 先push_back到堆尾 (数组末尾)
- 不断向上进行比较交换直到到根
- 详细看视频讲解
void push(const T &elem)
{
size++;
v.push_back(elem);
int root = size;
while (parent&&v[root] < v[parent])
{
swap(v[root], v[parent]);
root = parent;
}
}
删除
只讨论删除堆顶的 因为其他没什么意义
- 交换堆顶和堆尾
- 把堆尾在数组中pop_back掉
- 从堆顶开始向下进行调整 维护堆的三个性质
- 选左右儿子最大/最小的
- 与之比较 直到无法交换(没有儿子 或者已经符合性质)
void pop()
{
swap(v[1], v[size]);
v.pop_back();
size--;
int root = 1;
while (left<=size)
{
int now = left;
if (right <= size && v[right] < v[left])now = right;
if (v[root] < v[now])break;
swap(v[root], v[now]);
root = now;
}
}
查询
直接返回1号位就行了
T top() const
{
return v[1];
}
STL的优先队列
不要手写堆hh
#include<queue>
priority_queue<int> q; //默认大顶堆
priortiy_queue<int,vector<int>,greater<int>> q; //小顶堆 第二个模板参数是容器类型 第三个是比较
堆排序
符合以下的就能应用pop的调整方法来调整堆
- 除了堆顶不符合堆的性质 其他都符合
那么就可以从后往前对无序数组建堆
其中heap_adjust函数就是pop里面的那个向下调整的操作
const int n;
vector<int> arr(n+1); //待排序数组 下标从1开始
void heap_adjust(int root,int end) //root代表当前的需要调整的节点 root+1到end 都已调整好
{
int top=arr[root]; //当前子堆的顶端
for(int i=2*root;i<=end;i*=2) //更新出每个节点的左孩子
{
if(i<end&&arr[i]<arr[i+1])i++; //如果左孩子比右孩子小,则选择右孩子
if(top>=arr[i])break; //如果最开始的顶端比当前要大,证明已找到合适的位置插入
arr[root]=arr[i]; //把i位置的较大值升上顶
root=i; //更新下当前调整到的位置
}
arr[root]=top; //最后的插入
}
void build_heap()
{
for(int i=n/2;i>0;i--)heap_adjust(i,n); //先从后向前构建成大顶堆
}
建完堆之后:
- 不断交换堆顶和堆尾 (因为堆顶永远最大/最小)
- 对堆顶到堆尾-1进行调整成堆 (符合: 除了堆顶不符合堆的性质 其他都符合) 所以可以用adjust函数
- 最后就会大顶堆生成从小到大排序 小顶堆生成从大到小排序
void heap_sort()
{
build_heap();
//已经建堆完成
for(int i=n;i>1;i--){
swap(arr[1],arr[i]); //将堆顶和堆尾交换
heap_adjust(1,i-1); //把交换之后的重新调整
}
}
STL建立堆
STL提供了
make_heap(arr.begin(),arr.end(),greater<int>());
pop_heap(arr.begin(),arr.end(),greater<int>());
arr.pop_back();
sort_heap(arr.begin(),arr.end(),greater<int>());
这几个函数来原地进行堆的操作
注意:
- greater<int>()的括号 生成小顶堆
- pop_heap不会弹出元素 需要自己在容器上操作
- sort必须在对应的大小堆上操作