5.3.3.堆排序
堆:设有数据元素的集合(R1,R2,R3,...Rn)它们是一棵顺序二叉树的结点且有
Ri<=R2i 和Ri<=R2i+1(或>=)
堆的性质:堆的根结点上的元素是堆中的最小元素,且堆的每一条路径上的元素都是有序的。
堆排序的思想是:
1)heapdown调整堆:每次调整都是从父节点、左孩子节点、右孩子节点三者中选择最大者跟父节点进行交换(交换之后可能造成被交换的孩子节点不满足堆的性质,因此每次交换之后要重新对被交换的孩子节点进行调整)
2)heapbuild建初始堆(将结点[n/2],[ n/2]-1,...3,2,1分别调成堆)
3)heapsort
建立初始堆,
当未排序完时
输出堆顶元素,删除堆顶元素,将剩余的元素重新建堆。
方法:将堆顶元素与最后无序元素交换,调整堆顶元素。
堆排序:稳定的nlog(n),只需要一个临时空间。
每次可以在log(n)的时间复杂度内求无序数据的最值。应用在优先队列内。
例题:3110 二叉堆练习3
题目描述 Description
给定N(N≤500,000)和N个整数(较有序),将其排序后输出。
输入描述 Input Description
N和N个整数
输出描述 Output Description
N个整数(升序)
样例输入 Sample Input
5
12 11 10 8 9
样例输出 Sample Output
8 9 10 11 12
数据范围及提示 Data Size & Hint
对于33%的数据 N≤10000
对于另外33%的数据 N≤100,000 0≤每个数≤1000
对于100%的数据 N≤500,000 0≤每个数≤2*10^9
程序如下:
从小到大排序,建立大顶堆。
写法一:数组做全局变量
#include<stdio.h>
#include<iostream>
#include<stdlib.h>
using namespace std;
int n,len,a[500009];
void heapdown(int i){每次向下调整堆顶元素
int j;
j=2*i;
while(j<=n){ //可能多次进行,所以while语句调整
if(j<n&&a[j]<a[j+1])j++;
if (a[i]<a[j])swap(a[i],a[j]);
i=j;
j=2*i;
}
}
void heapbuild(){//建立初始堆
int i;
for(i=n/2;i>0;i--)heapdown(i);
}
void heapsort(){//堆排序
int i;
heapbuild(); //建立初始堆,堆顶元素最大
while(n>=2){
swap(a[1],a[n]);//将堆顶元素与最后无序元素交换
n--;//无序长度不断缩短
heapdown(1); //堆顶元素可能是个较小的数,向下调整
}
}
int main(){
scanf("%d",&n);
int i;
len=n;
for(i=1;i<=len;i++)
scanf("%d",&a[i]);
heapsort();
for(i=1;i<=len;i++)
printf("%d ",a[i]);
}
如果要进行多组数的堆排序,需要传递数组参数
写法二:数组作为参数传递。
#include<stdio.h>
#include<iostream>
#include<stdlib.h>
using namespace std;
int n,len,b[500009];
void heapdown(int a[],int i,int size){
int j;
j=2*i;
while(j<=size){
if(j<size&&a[j]<a[j+1])j++;
if (a[i]<a[j])swap(a[i],a[j]);
i=j;
j=2*i;
}
}
void heapbuild(int a[],int size){
int i;
for(i=size/2;i>0;i--)heapdown(a,i,size);
}
void heapsort(int a[],int size){
int i;
heapbuild(a,size);
while(size>=2){
swap(a[1],a[size]);
size--;
heapdown(a,1,size);
}
}
int main(){
scanf("%d",&n);
int i;
len=n;
for(i=1;i<=len;i++)
scanf("%d",&b[i]);
heapsort(b,len);
for(i=1;i<=len;i++)
printf("%d ",b[i]);
}