注意点:
1、输出上和王道代码有区别,数组升序排列使用大根堆,数组降序排列,使用小根堆。
2、初始化堆,从n/2开始down,时间复杂度为o(n)。
o(n)的复杂度证明
根据树高,在非叶子结点最底下层,因为堆是完全二叉树,刨去最底下一层的叶子节点,上面是一个满二叉树。
所以除去叶子结点,最下面一层共有n/2个结点,逐层上去,分别是n/4,n/8,一直到n/(2^h)=2。
从第二层开始,到最底下非叶子结点层,每次最大的堆调整次数从1到h。
可以表达为1n/(2^h)+2(n/(2^h)-1)+.....+n/4*h,再通过错位相减,累加可以证得。
#include<iostream>
using namespace std;
const int MaxN=100010;
int n,m;
int h[MaxN],Size;
void Small_down(int u){ //小根堆形式
int t=u; //t用记录当前子树中最小元素的下标,2*u为左子树,2*u+1为右子树;
if(2*u<=Size&&h[2*t]<h[t])t=2*u;
if(2*u+1<=Size&&h[2*u+1]<h[t])t=2*u+1;
if(u!=t){
swap(h[u],h[t]);
Small_down(t);
}
}
void Big_down(int u){ //大根堆形式
int t=u;
if(2*u<=Size&&h[2*t]>h[t])t=2*u;
if(2*u+1<=Size&&h[2*u+1]>h[t])t=2*u+1;
if(u!=t){
swap(h[u],h[t]);
Big_down(t);
}
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&h[i]);
Size=n;
for(int i=n/2;i>=1;i--)Small_down(i);
for(int i=1;i<n;i++){
swap(h[1],h[Size--]);
Small_down(1);
}
for(int i=1;i<n;i++)printf("%d ",h[i]);
}