今天学了一下堆排序。
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。
如果根节点的编号为i,那么它的左孩子的编号为2*i+1,右孩子的编号为2*i+2,
大根堆的要求是每个节点的值都不大于其父节点的值,
即Root[i].key>=Lchild[2*i+1].key&&Root[i].key>=Rchild[2*i+2].key
在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。
而小根堆指的是父节点的值都不大于其左右孩子的值,
即,Root[i].key<=Lchild[2*i+1].key&&Root[i].key<=Rchild[2*i+2].key,
那么最小值就在堆顶。
我们还应该知道什么是完全二叉树和满二叉树:
满二叉树是指如下所示的二叉树:
除最后一层外,每一层上的所有结点都有两个子结点。在满二叉树中,每一层上的结点数都达到最大值,即在满二叉树的第k层上有
完全二叉树是指如下所示的二叉树:
完全二叉树的节点除了最后一层外,节点都达到最大数,如果最后一层缺少叶子节点,一定是最右边的。
堆排序(升序,大顶堆)就是先把一个带排序的序列构建成堆,这样堆顶元素一定是序列中的最大值,然后把对顶元素和堆的最后一个元素交换,这样,最后一个元素就有序了。
经过堆顶元素和最后一个元素的交换,剩下的(n-1)元素就不再满足大顶堆的性质了,就再次把剩下的元素构建为堆,继续重复交换堆顶元素和最后一个元素,直到所有元素排序完毕。下图只展示了交换出8和9的过程:
#include<stdio.h>
void HeapAdjust(int a[],int i,int len);//堆调整
void PRINT(int a[],int len);//数组打印
void HeapSort(int a[],int len);//堆排序
void swap(int *a,int *b);//交换函数
int main()
{
int a[7]={3,6,1,0,5,7,2};
PRINT(a,7);
HeapSort(a,7);
PRINT(a,7);
return 0;
}
void PRINT(int a[],int len)//数组打印函数实现
{
int i;
for(i=0;i<len;i++)
{
printf("%d->",a[i]);
}
printf("\n");
}//PRINT函数结束
void swap(int *a,int *b)//交换函数
{
int t;
t=*a;
*a = *b;
*b = t;
}
void HeapAdjust(int a[],int i,int len)//堆调整函数,i为待调整节点编号,len为数组长度,根节点编号为0
{
int lc = 2*i + 1;//i节点的左孩子
int rc = 2*i + 2;//i节点的右孩子
int max = i;//假设最大值节点的下标为i
if(2*i+1<len)//只要待调整节点的左孩子的下标不超过数组长度
{
a[lc]>a[rc]?max=lc:max=rc;//max取得左右孩子中较大者的下标
if(max!=i)//如果max!=i,说明最大值下标与开始假设不一致,交换最大值到i节点处
{
int t;
t=a[max];
a[max]=a[i];
a[i]=t;
HeapAdjust(a,max,len);//递归调用,防止交换以后max节点不满足堆性质
}
}
}
void HeapSort(int a[],int len)
{
int i,j;
for(j=(len-2)/2;j>=0;j--)
{
HeapAdjust(a,j,len);//调整为堆
}
for(i=len-1;i>=2;i--)
{
swap(&a[0],&a[i]);//交换堆顶与最后一个元素
HeapAdjust(a,0,i-1);//重新调整剩余节点为堆
}
}