堆
堆
一、堆的定义
堆(英语:heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:
堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。
将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。
堆是非线性数据结构,相当于一维数组,有两个直接后继。
堆的定义如下:n个元素的序列{k1,k2,ki,…,kn}当且仅当满足下关系时,称之为堆。
(k_i <= k_2i , k_i <= k_(2i+1))或者( k_i >= k_2i, k_i >= k_(2i+1)), (i = 1,2,3,4…n/2)
二、手写一个堆
手写堆的存储方式:用一维数组存储树的每一个节点。
基本操作
(这里以小根堆为例)
对于不满足堆的性质的一个节点做调整:
down(向下调整):将它与它的左二子,右儿子比较大小,并与最小值交换位置(否则不满足堆的性质),递归的完成这个过程。(时间复杂度O($logN$))
1 2 3 4 5 6 7 8 9 10 11
| void down(int k) { int mink=k; if(2*k<=cnt and h[2*k]<h[mink])mink=2*k; if(2*k+1<=cnt and h[2*k+1]<h[mink])mink=2*k+1; if(mink!=k) { swap(h[mink],h[k]); down(mink); } }
|
up(向上调整):将它与它的父节点比较,如果比父节点要小,那么就与父节点交换,递归的完成这个过程。(时间复杂度O($logN$))
支持的功能:
插入一个数
求集合中的最大/小值
删除集合中的最大/小值
1 2
| heap[1]=heap[cnt]; cnt--; down(1);
|
删除任意一个元素
1 2
| heap[k]=heap[cnt];cnt--; down(k);up(k);
|
修改任意一个元素
1 2
| heap[k]=x; down(k),up(k);
|
由数组建堆(建堆复杂度看似O($NlogN$),实际可求出为O(N))
1 2
| for(int i=1;i<=n;i++)cin>>h[i]; for(int i=n/2;i>=1;i--)down(i);
|
三、模板和例题
洛谷P1177 快速排序:用堆排序来做
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| #include<iostream> #include<cstdio> using namespace std; const int N=100050; int n,m; int cnt=0; int h[N]; void down(int k) { int mink=k; if(2*k<=cnt and h[2*k]<h[mink])mink=2*k; if(2*k+1<=cnt and h[2*k+1]<h[mink])mink=2*k+1; if(mink!=k) { swap(h[k],h[mink]); down(mink); } } int main() { ios::sync_with_stdio(0); cin>>n; for(int i=1;i<=n;i++) { cin>>h[i]; } cnt=n; for(int i=n/2;i>=1;i--) { down(i); } while(n--) { cout<<h[1]<<" "; h[1]=h[cnt]; cnt--; down(1); } return 0; }
|
洛谷P3378 【模板】堆
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| #include<iostream> #include<cstdio> using namespace std; const int N=1000050; int n,m; int cnt=0; int h[N],ph[N]; void down(int k) { int mink=k; if(2*k<=cnt and h[2*k]<h[mink])mink=2*k; if(2*k+1<=cnt and h[2*k+1]<h[mink])mink=2*k+1; if(mink!=k) { swap(h[k],h[mink]); down(mink); } } void up(int k) { while((k/2) and h[k/2]>h[k]) { swap(h[k/2],h[k]); k/=2; } } void ins(int x) { h[++cnt]=x; up(cnt); } int main() { ios::sync_with_stdio(0); cin>>n; int op; int x; while(n--) { cin>>op; if(op==1) { cin>>x; ins(x); } else if(op==2) { cout<<h[1]<<endl; } else { h[1]=h[cnt]; cnt--; down(1); } } return 0; }
|