(二叉)堆排序

完全二叉树

完全二叉树,是一种二叉树,并且具有以下性质:

如果树的某层不是最后一层,那一定是填满的;
最后一层总是从左往右排列,既从左子树开始排列;

完全二叉树特性:

对于某节点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");

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值