/*2019-7-4-13:56----------------------------woc,我之前写的都是些什么玩意。。--------------------------------------------------------------*/
所谓堆(二叉,这里指的是二叉堆)这个东西,可以取出最大或者最小值,得删除
插入 | 删除 | 取最值 |
也就是说,这个数据结构可以用来维护一个最值问题
以下是一个大根堆的例子
我们拿数组作为一个完全二叉树来储存堆
1、插入
就是,放到数组最后,然后堆调整上去(一路上和他的父亲比较)
(图是一个大根堆的图,来源、,下面图同样来源)
2、删除
就是把最后一个元素赋值给堆顶,然后堆调整下去(一路上和他的两个儿子比较,每次和最大的儿子交换)
下面是一份小根堆的代码
int tot = 1;//
void Insert(int data,int pos = tot+1){
tree[++tot] = data;
while(tot!=1 && tree[tot]<tree[tot>>1])
swap(tree[tot],tree[tot>>1]),pos>>=1;
}
void del(int pos = 1){//
tree[1] = tree[tot--];
while(true){
int tpos = pos;
if((pos<<1)<=tot && tree[tpos]>tree[pos<<1])tpos = pos<<1;
if((pos<<1|1)<=tot && tree[tpos]>tree[pos<<1|1])tpos = pos<<1|1;
if(pos == tpos)break;
swap(tree[pos],tree[tpos]),pos=tpos;
}
}
int Top(){
return tree[1];
}
对于堆排序就是,先把大根堆建起来,然后每次取出堆顶(最大值),删掉(然后放到数组最后)
最后堆为空,数组有序
时间复杂度分析
建堆
取堆顶n次,删除次,共
综合起来
空间复杂度,用的是原来的数组,并没有额外开数组
时间 | 空间 |
这是一份洛谷p1177快速排序的代码
实测挺快的,也就比我用c++的sort稍微慢一点,比我手写的combsort和shellsort快,但是比Mergesort慢一丁点
也就几ms的差距,他们几个差距不是很大
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+7;
int heap[maxn],tot,n;
void Insert(int data,int pos = tot+1){//small heap
heap[++tot] = data;
while(pos!=1 && heap[pos]<heap[pos>>1])
swap(heap[pos],heap[pos>>1]),pos>>=1;
}
void del(int pos = 1){
if(!tot)return;
heap[1] = heap[tot--];
while(true){
int Next = pos;
if((pos<<1)<=tot && heap[Next]>heap[pos<<1])Next = pos<<1;
if((pos<<1|1)<=tot && heap[Next]>heap[pos<<1|1])Next = pos<<1|1;
if(Next == pos)break;
swap(heap[pos],heap[Next]),pos = Next;
}
}
int main(){
scanf("%d",&n);
for(int i=1,x;i<=n;i++)scanf("%d",&x),Insert(x);
for(int i=1;i<=n;i++,del())printf("%d%c",heap[1],i==n?'\n':' ');
return 0;
}
/*---------------------------------------------------------分-------------割-------------线-------------------------------------------------------------------------*/
所谓的堆就是一颗完全二叉树,
满足
1对root节点都大于等于(小于等于)其左右子节点
2对其每个子树,都是一个堆(也就是递归定义了呗)
其实就是任取一个点,这个点都满足---大于等于(小于等于)其左右子节点(如果有的话)
那么根据完全二叉树性质i的左子节点是2*i,右节点是2*i+1,那么就可以在数组里进行操作了(*)
将一个数组看成一颗完全二叉树,注意这里应该舍弃0节点,因为其不符合(*)
不妨建立大根堆
先建立一个大根堆,那么取出其根节点,与数组中最后一个(假设叫t好了)交换,
相当于删除了大根堆中的根节点,
看怎么调整,让剩下的仍为大根堆,
首先将t放到根节点位置,然后下调,
也就是将左右子节点中大的那个,提上来(与t交换)
递归向下,直到叶子(或者t不能下移,即t放到了他应该在的位置),
这一步log级别(树的高度),每一步取出当前堆中根,也就是最大元素,放到数组末尾,
当取完所有元素,排序即完成
复杂度n*log(n)
但是我们一般用的数组都是从0开始的,所以,重新找规律,
i的左子节点等于2*i+1,右:2*i+2(左+1)
void swap(int &a,int &b){
int c = a;
a = b,b = c;
}
void heapcr(int *a,int rt,int mx){
int l = rt<<1|1,maxid = rt;
int r = l+1;
if(l<mx && a[l]>a[rt])
maxid = l;
if(r<mx && a[maxid]<a[r])
maxid = r;
if(maxid != rt){
swap(a[rt],a[maxid]);
heapcr(a,maxid,mx);
}
}
void Heapsort(int *a,int len){
for(int i=len/2-1;i+1;i--)
heapcr(a,i,len);
for(int i=len-1;i;i--){
swap(a[0],a[i]);
heapcr(a,0,i);
}
}