堆的性质:其实质是二叉树的排序方式。用二叉树的逻辑映射数组。
例如:一个数组有10个元素,a[0]的左子树是a[1], 右子树是a[2],以此类推a[i]的左右子树分别是a[2*i+1], a[2*i+2]。
在堆排序时,堆的性质是(1) ki≤K2i且ki≤K2i+1 或(2)Ki≥K2i且ki≥K2i+1(1≤i≤),本质是父节点要大于或小于其子节点。
数组与树的一个映射关系,如果一个节点的ID是i 那么它左子节点对应的数组下标的为 2*i + 1 右子节点对应的数组下标为 2*i + 2
堆排序的过程:
1) 整理一个数字序列,使其具有堆得性质 (1) ki≤K2i且ki≤K2i+1 或(2)Ki≥K2i且ki≥K2i+1(1≤i≤)。
2) 把第一个元素(整理以后最大或最小的元素会放到第一个)和当前最后(注意当前最后一个,是一个动态的描述,它在不断的向前缩进)
一个元素交换,保证当前的最后一个位置的元素都是在现在的这个序列之中最大的。 交换后再进行调整,保证堆的性质。
#include <stdio.h>
void adjust_tree(int* arr, int idx, int length)
{
int nTmp=0;
int nChild=0;
for (nTmp=arr[idx]; idx*2+1<length; idx=nChild)
{
nChild = idx*2+1;
// compare the left child and right child
if ((nChild+1 != length)&&(arr[nChild+1] > arr[nChild]))
nChild++;
if (nTmp < arr[nChild])
{
arr[idx]=arr[nChild];
}
else
{
break;
}
}
arr[idx] = nTmp;
}
void heap_sort(int* arr, int len)
{
int i = 0;
for (i=len/2-1; i>=0; i--)
{
adjust_tree(arr, i, len);
}
// 排序范围不断向前缩进
for (i=len-1; i>0;i--)
{
arr[0]^=arr[i];
arr[i]^=arr[0];
arr[0]^=arr[i];
adjust_tree(arr, 0, i);
}
}
int main()
{
int testArr[] = {4,2,5,1,8,6,3,9,0,7};
int i = 0;
for (i =0; i<10;i++)
{
printf("%d ", testArr[i]);
}
printf("\n\n");
heap_sort(testArr, 10);
for (i=0; i < 10; i++)
{
printf("%d ", testArr[i]);
}
printf("\n\n");
printf("%d", &i);
return 0;
}
算法分析:
堆排序是不稳定算法,堆排序的时间,主要由建立初始堆和反复重建堆这两部分的时间开销构成,
一个有n完全二叉树的高度为log2N,那么在最坏情况下n个元素要从树根一只比较到最底层的节点
N*log2N,是最坏的情况。