完全二叉树
完全二叉树,是一种二叉树,并且具有以下性质:
如果树的某层不是最后一层,那一定是填满的;
最后一层总是从左往右排列,既从左子树开始排列;
完全二叉树特性:
对于某节点i:
若果有父结点,则父结点为i/2。(/为整除,下同)
若有左孩子结点,则为2*i。
若有右孩子结点,则为2*i+1.
含有n个元素的完全二叉树高度为 log2(n+1)向下取整。
二叉堆和最大堆
二叉堆
用完全二叉树构造的堆,通过数组之间的下标联系实现二叉堆。如:
根据二叉堆的性质,结点1的两个孩子分别是2和3,那么在数组中,下标1可以表示结点1,下标2和3表示的就是结点1的两个孩子,结点2的两个孩子在数组中又分别下标为4和5,以此类推,就可以轻松哎一维数组中表示一个二叉堆。
最大堆
父结点总不比其孩子结点小的堆为最大堆。堆排序主要用到最大堆得性质。
堆排序
利用二叉堆的结构特点来排序。
基本思想
维持最大堆
对于以结点i为根的子树,我们试图以某些方法保证该子树符合最大堆的性质(即有序),并且把叶子结点看成是一个符合最大堆性质的子树,保证结点i的两个子树都是有序的。
首先拿i和其两个孩子比较,若结点i最大,因为i的两个子树都是有序的,则子树i是有序的,结束操作。
若i不是最大的,则i结点的内容和最大的一个孩子(假设是左孩子2*i)对换,保证了i和2*i和2*i+1是有序的,这样却可能破坏了左孩子所形成的子树的有序性,所以再对左孩子子树递归的调用维持最大堆的算法即可。
建立最大堆
如何对一个数组建立最大堆?即保证数组有序。上文说道,叶子结点可以看作是一个有序的子树,我们可以从最后一个有叶子结点的父结点出发,调用维持最大堆的子程序,从下到上地不断维持子树有序,最后实现整个树有序。
排序
1、建立最大堆,此时树的根即是数组中最大的元素
2、将根与最后一个叶子结点交换,并且舍弃最后一个叶子结点,
3、若此时堆中只有两个个元素,停止程序,否则对根调用维持最大堆子程序,执行2
这样下来,当前堆中最大的元素总是不断被放到堆得末尾被舍弃,就能依次得到从小到大的有序序列。
代码实现
#include<stdio.h>
int heapsize;
void build_max_heap(int *a,int length)//建立最大堆,数组从下标1开始
{
int i;
heapsize=length;
for(i=length/2;i>=1;i--)
{
max_heap(a,i);
}
}
void max_heap(int *a,int i)//保持子堆为最大堆
{
int l,r,temp;
int max;//三个节点中最大元素的下标
l=2*i;
r=2*i+1;
if(l<=heapsize&&a[l]>a[i])
max=l;
else
max=i;
if(r<=heapsize&&a[r]>a[max])
max=r;
if(max!=i)
{
temp=a[i];
a[i]=a[max];
a[max]=temp;
max_heap(a,max);
}
}
void heapsort(int *a,int length)//堆排序
{
int temp,i;
build_max_heap(a,length);//建立最大堆
for(i=length;i>=2;i--)
{
temp=a[1];
a[1]=a[i];
a[i]=temp;
heapsize--;
max_heap(a,1);//保持剩余堆为最大堆
}
}
int main()
{
int i;
int a[11]={7,9,4,8,5,6,1,55,7,12,3};
heapsort(a,10);//排序
for(i=1;i<=10;i++)
printf("%d ",a[i]);
printf("\n");
}