二叉堆是完全二叉树或者是近似完全二叉树。
二叉堆满足二个特性:
1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。
2.每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。
当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆。当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆。下图展示一个最小堆:
由于其它几种堆(二项式堆,斐波纳契堆等)用的较少,一般将二叉堆就简称为堆。
堆得存储
一般都用数组来表示堆,i结点的父结点下标就为(i – 1) / 2。它的左右子结点下标分别为2 * i + 1和2 * i + 2。如第0个结点左右子结点下标分别为1和2。
堆化数组
重上图看: 叶子结点 20 ,60 ,65 ,4 ,19都可以认为是一个合法的堆。
现在只要充A[4] = 50 向下调整。然后再A[3]=30,A[2] = 17,A[1] = 12,A[0] = 9分别作一次向下调整操作就可以了
初始堆建立完毕,然后就是排序。
堆排序
首先可以看到堆建好之后堆中第0个数据是堆中最小的数据。取出这个数据再执行下堆的删除操作。这样堆中第0个数据又是堆中最小的数据,重复上述步骤直至堆中只有一个数据时就直接取出这个数据。类似于选择排序。
第一次将A[0]与A[n - 1]交换,再对A[0…n-2]重新恢复堆。第二次将A[0]与A[n – 2]交换,再对A[0…n - 3]重新恢复堆,重复这样的操作直到A[0]与A[1]交换。由于每次都是将最小的数据并入到后面的有序区间,故操作完成后整个数组就有序了。
#include <stdio.h>
//调整堆
void heapAdjust(int a[] ,int s,int m){
int rc = a[s];
for(int j = 2*s ;j<=m ;j*=2){
if(j<m&&a[j]>a[j+1]){
++j;
}
if(rc <= a[j]){
break;
}
a[s] = a[j];
s = j;
}
a[s] = rc;
}
void heapSort2(int a[] ,int n){
//建立初始堆。
//从n/2 开始因为树的 最后一个非叶子结点就是2/n
for(int i = n/2 ;i>0 ;--i){
heapAdjust(a,i,n);
}
int t;
t = a[1];
a[1] = a[n];
a[n] = t;
for(int i = n-1 ;i>1;--i){
heapAdjust(a,1,i);
t = a[1];
a[1] = a[i];
a[i] = t;
}
}
void print(int a[] ,int n)
{
for (int i =1;i<n;i++)
{
printf("%d ",a[i]);
}
printf("\n");
}
int main(int argc, char *argv[])
{
int a[]=
{
0,59,48,75,98,86,23,37,60
};
print(a,9);
heapSort2(a,8);
print(a,9);
return 0;
}