堆是一棵完全二叉树
小根堆 则每个结点<=左右孩子
则某个点值变大了 就要向下沉down() 时间复杂度O(logn)
某个点值变小了 就要像上升up() 时间复杂度O(logn)
堆排序是不稳定的:
比如:3 27 36 27,
如果堆顶3先输出,则,第三层的27(最后一个27)跑到堆顶,然后堆稳定,继续输出堆顶,是刚才那个27,这样说明后面的27先于第二个位置的27输出,不稳定。
实现堆排序
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int h[N],Size;
void down(int i){
int t=i;
if(2*i<=Size&&h[2*i]<h[i]) t=2*i;
if(2*i+1<=Size&&h[2*i+1]<h[t]) t=2*i+1; t为中左右最小节点的下标
if(t!=i){
swap(h[t],h[i]); 交换
down(t); 递归
}
}
int main(){
int n,m;
cin>>n>>m;
Size=n;
for(int i=1;i<=n;i++){ 要从1开始
scanf("%d",&h[i]);
}
for(int i=n/2;i;i--) down(i); 建堆n/2~1 时间复杂度O(n)(不是nlogn)
while(m--){
cout<<h[1]<<" "; 输出最小元素
h[1]=h[Size]; 删堆顶
Size--;
down(1);
}
}
需要对第k个插入的数进行操作 需要再维护ph[] hp[]两个数组
ph[k]代表第k个数的下标是什么 hp[i]代表下标为i的数第几个
堆中特有的heap_swap 删除操作用heap_swap(1,Size); 而不是只赋值
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int h[N],ph[N],hp[N],Size,cnt;
void heap_swap(int a,int b){
swap(ph[hp[a]],ph[hp[b]]);
swap(hp[a],hp[b]);
swap(h[a],h[b]);
}
void down(int x){
int t=x;
if(2*x<=Size&&h[2*x]<h[t]) t=2*x; %%后为t
if(2*x+1<=Size&&h[2*x+1]<h[t]) t=2*x+1; %%后为t
if(t!=x){
heap_swap(x,t);
down(t);
}
}
void up(int x){
while(x/2&&h[x]<h[x/2]){
heap_swap(x,x/2);
x/=2;
}
}
int main(){
int n; cin>>n;
string a;
while(n--){
cin>>a;
if(a=="I"){
int x; cin>>x;
Size++;
cnt++;
h[Size]=x;
ph[cnt]=Size;
hp[Size]=cnt;
up(Size);
}
if(a=="PM"){
cout<<h[1]<<endl;
}
if(a=="DM"){
heap_swap(1,Size);
Size--;
down(1);
}
if(a=="D"){
int k; cin>>k;
k=ph[k]; //要先预存k=ph[k] 因为ph[k]在swap后变了
heap_swap(k,Size);
Size--;
up(k);
down(k);
}
if(a=="C"){
int k,x; cin>>k>>x;
k=ph[k];
h[k]=x;
up(k);
down(k);
}
}
}