我们学习堆排序之前需要弄清堆的概念,计算机常说的堆有两种一种是操作系统上的堆内存,还有一种是数据结构上的堆,今天我们来讲数据结构上的堆,数据结构上的堆也有几种,二叉堆,二项堆,斐波那契堆,通常我们把二叉堆成为简称为堆。
堆排序也是一种效率很高的排序,平均时间复杂度为:O(nlogn)
二叉堆是一种完全二叉树,其中非叶结点与父节点的关系总满足,i<=2*1+1&&i<=2*(i+1)或者i>=2*1+1&&i>=2*(i+1)的关系,i为父节点
其中,i<=2*1+1&&i<=2*(i+1)称为小顶堆,i>=2*1+1&&i>=2*(i+1)称为大顶堆。
堆排序的思想如下:
首先将指定的一组数转化成大顶堆或者小顶堆,通过父节点与子节点的比较进行前后替换,通过递归遍历所有节点,每次从顶部取出未排序的最大值跟未排序的最后一个交换, 例如:堆A[0,n-1],先交换a[0]和A[n-1],调整堆,交换A[0]和A[n-2],调整堆......,交换A[0]和a[1],交换完毕。
得出排序结果。
(初始化堆,循环(交换顶部和未排序的最后一个,调整堆)。
举个例子:
第一步、给个数组a[4]={3,1,2,4}
转换得一二叉树
3
1 2
4
第二步、堆化,从第一个非叶结点开始调整,左子节点,1和4交换得
3
4 2
1
第三步继续堆化,3跟4交换得初始大顶堆
4
3 2
1
下面为关键步骤:
第四步,将最顶的与最后的交换,把最大的放到最后面的节点。
1
3 2
4
交换了之后,明显1和3不符合堆的概念,因为需要继续调整
3
1 2
4
最顶的和未排序的最后一个交换,3和2交换得
2
1 3
4
2和1交换得
1
2 3
4
节点访问完毕,得结果。
关键代码:
void max_heapify(int*arr,int index,int len)
{
int l=2*index+1;
int r=2*(index+1);
int largest;
if(l<len&&arr[l]>arr[index])
largest=l;
else
largest=index;
if(r<len&&arr[r]>arr[largest])
largest=r;
if(largest!=index)
{
//将最大元素提升,并递归
myswap(&arr[largest],&arr[index]);
max_heapify(arr,largest,len);
}
}
下面给出完整代码:
// List.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include<stdlib.h>
#include <time.h>
#define NUMLEN 20000
inline void myswap(int*a,int*b)
{
*a=*a^*b;
*b=*a^*b;
*a=*a^*b;
}
void max_heapify(int*arr,int index,int len)
{
int l=2*index+1;
int r=2*(index+1);
int largest;
if(l<len&&arr[l]>arr[index])
largest=l;
else
largest=index;
if(r<len&&arr[r]>arr[largest])
largest=r;
if(largest!=index)
{
//将最大元素提升,并递归
myswap(&arr[largest],&arr[index]);
max_heapify(arr,largest,len);
}
}
void build_maxheap(int*arr,int len)
{
//建立大顶堆
int i;
if(arr==NULL||len<=1)
return;
for(i=len/2+1;i>=0;--i)
max_heapify(arr,i,len);
}
void heap_sort(int*arr,int len)
{
int i;
if(arr==NULL||len<=1)
return;
build_maxheap(arr,len);
for(i=len-1;i>=1;--i){
myswap(&arr[0],&arr[i]);
max_heapify(arr,0,--len);
}
}
int main(int argc, char* argv[])
{
int a[NUMLEN];
srand((int)time(NULL));
for(int i=0;i<NUMLEN;i++)
a[i]=rand()%NUMLEN;
float nstart=clock();
heap_sort(a,NUMLEN);
for(i=0;i<NUMLEN;i++)
printf("%d\n",a[i]);
float nend=clock();
printf("共用时%.3fs\n",(nend-nstart)/1000);
return 0;
}
20000个数据的时间情况: