(数据结构)堆
Posted pisceskkk
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(数据结构)堆相关的知识,希望对你有一定的参考价值。
简介
堆,一般通过二叉树实现,且保证根节点优先级总是高于(或低于)两个子节点。
二项式堆、斐波那契堆见补充部分。
基本性质
- 根节点优先级总是高于(或低于)两个子节点。
- 对于节点\(i\),其两个子节点分别是 \(i*2\) 和 \(i*2+1\),即 \(i<<1\) 和 \(i<<1|1\)。
- 为了简洁,令
heap[0]
作为 heap_size 存储 堆的大小。
功能
- 堆排序(直接在乱序
heap[]
数组基础上)——heapsort
- 清除堆 ——
clear
- 插入数据 ——
push
- 取根 ——
top
- 删除根 ——
pop
- 更新,为了维护某一节点的性质 ——
update
代码实现
//////////////////////////////////////////////////////////////////////
//Target: 洛谷 P3378 【模板】堆
//@Author: Pisceskkk
//Date: 2019-5-6
//////////////////////////////////////////////////////////////////////
#include<cstdio>
#include<cstring>
#define N 1000010
using namespace std;
int n,heap[N]; //小根堆
void update(int x){
while(x > 1 && heap[x>>1] > heap[x]){ // 不要让x更新到1!
heap[x>>1] ^= heap[x] ^= heap[x>>1] ^= heap[x];
x >>= 1;
}
}
void heapsort(int len){
heap[0] = len;
for(int i=1;i<len;i++){
update(i);
}
}
void clear(){
memset(heap,0x7f,sizeof(heap));
heap[0]=0;
}
void push(int x){
heap[++heap[0]] = x;
update(heap[0]);
}
int top(){
return heap[1];
}
void pop(){
int x=1;
heap[1] = heap[heap[0]]; //取最后一个元素补上去,然后维护。这样可以保证heap[0] 位置可以删掉。
heap[0] --;
while((x<<1) <= heap[0]){
x = (heap[x<<1] < heap[x<<1|1]) ? (x<<1) : (x<<1|1);
if(heap[x] < heap[x>>1])heap[x>>1] ^= heap[x] ^= heap[x>>1] ^= heap[x];
}
}
int main(){
scanf("%d",&n);
clear();
int x;
while(n--){
scanf("%d",&x);
if(x == 1){
scanf("%d",&x);
push(x);
}
else if(x == 2){
printf("%d\n",top());
}
else if(x == 3){
pop();
}
}
return 0;
}
补充
以下部分大多来自维基百科
二项堆
二项堆更方便实现两个堆的合并。
其递归定义如下:
- 度数为0的二项树只包含一个结点
- 度数为k的二项树有一个根结点,根结点下有 \(k\)个子女,每个子女分别是度数分别为 \(k-1,k-2,...,2,1,0\) 的二项树的根
图示(来源自维基百科):
度数为 \(k\) 的二项树共有 \(2^{k}\)个结点,高度为 \(k\)。在深度d处有 \(\tbinom {k}{d}\)(二项式系数)个结点。
度数为k的二项树可以很容易从两颗度数为k-1的二项树合并得到:把一颗度数为k-1的二项树作为另一颗原度数为k-1的二项树的最左子树。这一性质是二项堆用于堆合并的基础。
实现详见二项堆的操作
斐波那契堆
简介
斐波那契堆(Fibonacci heap)是计算机科学中树的集合。它比二项堆具有更好的平摊分析性能,可用于实现合并优先队列。不涉及删除元素的操作有O(1)的平摊时间。 Extract-Min和Delete的数目和其它相比,较小时效率更佳。稠密图每次decrease key只要O(1)的平摊时间,和二项堆的O(lg n)相比是巨大的改进。
斐波纳契堆于1984年由Michael L. Fredman与Robert E. Tarjan提出,1987年公开发表。[1]名字来源于运行时分析使用的斐波那契数。
结构
斐波那契堆是由一组最小堆有序树构成的。每个节点的度数为其子节点的数目。树的度数为其根节点的度数。
斐波那契堆中的树都是有根的但是无序。每个节点x包含指向父节点的指针p[x]和指向任意一个子结点的child[x]。x的所有子节点都用双向循环链表链接起来,叫做x的子链表。子链表中的每一个节点y都有指向它的左兄弟的left[y]和右兄弟的right[y]。如果节点y是x仅有的子节点,则left[y]=right[y]=y。
斐波那契堆中所有树的根节点也用一个双向循环链表链接起来。
使用一个指针指向斐波那契堆中最小元素。
实现
详见斐波那契堆操作
以上是关于(数据结构)堆的主要内容,如果未能解决你的问题,请参考以下文章