排序(堆排序)

1、什么是堆


通过上面的图片可以看出,堆其实是一棵 完全二叉树。每个结点的值都大于或等于其左右两个子结点,称为 大顶堆;每个结点的值小于或等于其左右子结点,称为 小顶堆。既然是一个完全二叉树,堆结构自然也是严格遵守二叉树的一些规则: 如果按从上至下,由左及右的层次遍历给每个结点编号,从1开始,那么编号为1的结点就是根结点,其左右子结点的编号分别为2i和2i+1。

2、堆排序算法

堆排序(Heap Sort)就是利用堆结构进行排序(假设利用大顶堆),其基本思想是先将待排序的序列构造为一个大顶堆,将其根结点(最大值)提取出来,再将剩下的待排序的子序列重新构造出一个堆,再将新堆的根结点(第二大的值)提取出来。如此反复的重构堆、提取。直到最后一个结点为止。
综上所述,只需要解决两个问题,就可以完成堆排序
a、如何将一个无序的序列构造成一个堆?
b、将堆顶提取之后,如何将子序列重构为一个新的堆?

3、C语言实现

#include<stdio.h>

/*交换两个数的值*/
void swap(int *a,int *b)
{
    int temp=*a;
    *a=*b;
    *b=temp;
}

/*大顶堆的重构*/
void HeapAdjust(int *arr,int i,int n)
{
    int temp,j;
    temp=arr[i];
    for(j=i*2; j<=n; j*=2)/*从当前关键字的左右子结点向下筛选*/
    {
        if(j<n&&arr[j]<arr[j+1])
            ++j;/*记录较大值的下标*/
        if(temp>=arr[j])
            break;/*当寻找到合适的插入位置,停止筛选*/
        arr[i]=arr[j];
        i=j;
    }
    arr[i]=temp;/*插入关键字*/
}

/*堆排序*/
void HeapSort(int *arr,int n)
{
    int i;
    for(i=n/2; i>0; i--)/*将待排序的序列构建为一个堆*/
        HeapAdjust(arr,i,n);
    for(i=n; i>1; i--)
    {
        swap(arr+1,arr+i);/*将堆顶元素与当前未排序的子序列中的最后一个元素交换*/
        HeapAdjust(arr,1,i-1);/*交换后,将未排序的子序列重构为一个合法的堆*/
    }

}


int main()
{
    int arr[]= {-1,50,20,30,40,10,80,70,90,60};
    int i;
    HeapSort(arr,9);
    for(i=1; i<=9; i++)
        printf("%d ",arr[i]);
}


4、堆排序的复杂度分析

堆排序的运行时间主要消耗在初始化堆与堆重构时的反复筛选这一个过程。总体来说,堆排序的时间复杂度是O(nlogn),由于堆排序并不在意最初始的记录状态,所以无论是最好情况,最坏情况还是一般情况其时间复杂度都是O(nlogn)。空间复杂度上,它也只是利用一个临时的变量,所以其空间复杂度是O(1)。可以看出,在堆重构的过程,数据之间的比较和交换不是线性,可以这也是一个种不稳定的排序。堆排序并不适用于数量较少的序列。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值