1.小根堆
如果根是儿童的存在留下的根值左孩子小于值;如果根是儿童的权利的存在的根值比他们的孩子的权利少值。
2.大根堆
如果根是儿童的存在留下的根值多名离开自己的孩子值。子女则根节点的值大于右子女的值。
3.结论
(1)堆是一棵全然二叉树(假设公有h层,那么1~h-1层均满,在h层连续缺失若干个右叶子)。
(2)小根堆的根节点的值是最小值,大根堆的根节点的值是最大值。
(3)堆适合于採用顺序存储。
4.堆的插入算法
将一个数据元素插入到堆中,使之依旧成为一个堆。
算法描写叙述:先将结点插入到堆的尾部,再将该结点逐层向上调整,直到依旧构成一个堆。调整方法是看每一个子树是否符合大(小)根堆的特点,不符合的话则调整叶子和根的位置。
5.堆的删除算法
堆在删除元素时,仅仅能够删除根节点。
算法描写叙述:将根节点删除后用堆尾结点进行填补,调整二叉树,使之依旧成为一个堆。
6.堆排序(大根堆,小根堆类似)
其基本思想为(大根堆):
1)将初始待排序keyword序列(R1,R2....Rn)构建成大顶堆,此堆为初始的无序区,构建的过程是每一个非叶子结点都经过一次调整,调整顺序为从底层至顶层(调整过程中含有递归),这样调整下来这个二叉树总体上就是一个大根堆(或小根堆)了;
2)将堆顶元素R[1]与最后一个元素R[n]交换。此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R[n];
3)因为交换后新的堆顶R[1]可能违反堆的性质,因此须要对当前无序区(R1,R2,......Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区(Rn-1,Rn)。不断反复此过程直到有序区的元素个数为n-1,则整个排序过程完毕。
操作步骤例如以下:
1)初始化堆:将R[1..n]构造为堆;
2)将当前无序区的堆顶元素R[1]同该区间的最后一个记录交换,然后将新的无序区调整为新的堆。
因此对于堆排序,最重要的两个操作就是构造初始堆和调整堆,其实构造初始堆其实也是调整堆的过程,仅仅只是构造初始堆是对全部的非叶节点都进行调整。
操作过程图示:
从上述过程可知。堆排序其实也是一种选择排序,是一种树形选择排序。
仅仅只是直接选择排序中。为了从R[1...n]中选择最大记录,需比較n-1次,然后从R[1...n-2]中选择最大记录需比較n-2次。其实这n-2次比較中有非常多已经在前面的n-1次比較中已经做过。而树形选择排序恰好利用树形的特点保存了部分前面的比較结果。因此能够降低比較次数。对于n个keyword序列。最坏情况下每一个节点需比較log2(n)次。因此其最坏情况下时间复杂度为nlog2(n)。堆排序为不稳定排序,不适合记录较少的排序。
关于log2(n)的理解:依据堆排序的过程,每次将大根堆根节点的值跟最后一个叶子的值进行交换,那假设最后的叶子结点正好是最小的数。那么这个叶子结点就会一层层的被放到子树终于放到叶子结点的位子(不是前面的叶子结点的位置了)。这种话这个叶子结点经过的层数就刚好为log2(n)。然而其它没有交换的二叉树的分支,由于曾经都是大根堆。所以大根堆的性质还是没有变化,这一点对理解程序至关重要。C语言程序例如以下:
/*堆排序(大根堆)*/
#include
/*注意:这个函数仅仅会在调整被交换的位置为大根堆。未交换的分支不会处理,
所以不能将一个非大根堆二叉树的根结点传递过来让这个函数将其处理为大根堆*/
void heap_ajust(int *a, int i, int size) /*a为堆存储数组,size为堆的大小*/
{
int lchild = 2*i; //i的左孩子节点序号
int rchild = 2*i +1; //i的右孩子节点序号
int max = i; /*存放三个顶点中最大的数的下标*/
int temp;
if(i <= size/2) //假设i是叶节点就不用进行调整
{
if(lchild<=size && a[lchild]>a[max])
{
max = lchild;
}
if(rchild<=size && a[rchild]>a[max])
{
max = rchild;
}
if(max != i)
{
temp = a[i]; /*交换a[i]和a[max]的值*/
a[i] = a[max];
a[max] = temp;
heap_ajust(a, max, size); /*被交换的位置曾经是大根堆,如今可能不是大根堆
所以须要又一次调整使其成为大根堆结构*/
}
}
}
void build_bheap(int *a, int size) /*建立大根堆*/
{
int i;
for(i=size/2; i >= 1; i--) /*非叶节点最大序号值为size/2*/
{
heap_ajust(a, i, size); /*每一个非叶子结点都须要调用这个函数*/
}
}
void heap_sort(int *a, int size) /*堆排序*/
{
int i;
int temp;
build_bheap(a, size);
for(i=size; i >= 1; i--)
{
temp = a[1];
a[1] = a[i];
a[i] = temp; /*交换堆顶和最后一个元素,即每次将剩余元素中的最大者放到最后面*/
heap_ajust(a, 1, i-1); /*又一次调整堆顶节点成为大顶堆,仅仅有被交换的分支才有可能不是大根堆*/
}
}
int main(int argc, char *argv[])
{
int a[]={0,16,20,3,11,17,8};
int size = sizeof(a)/sizeof(int) -1;
int i;
printf("size = %d\n", size);
heap_sort(a, size);
printf("Sort over:");
for(i=1;i <= size; i++)
printf("%d ", a[i]);
printf("\n");
return 0;
}
程序执行截图为: