详解堆排序

1.堆排序定义

堆排序是一种树形选择排序方法,特点是在排序的时候,可以将拥有n个关键字的数组或其他结构看做一个完全二叉树

如图:

堆的定义:n个关键字序列L(1...n)称为堆,当满足:

  • L(i) <= L(2i) 且 L(i) <= L(2i+1)   ( 1 <= i <= [n/2] ) 也就是说小于左右孩子 ,称之为小顶堆
  • L(i) >= L(2i) 且 L(i) >= L(2i+1)   ( 1 <= i <= [n/2] ) 也就是说大于左右孩子 ,称之为大顶堆

2.堆的性质

因为堆是一种基于完全二叉树的结构,所以具有完全二叉树的性质,如n个节点的完全二叉树:

  • 左孩子为2n
  • 右孩子为2n+1
  • 父节点为[ n/2 ]
  • 在顺序存储的完全二叉树中,非终端节点的编号i<=[n/2]

3.堆排序算法(以大根堆为例)

第一步:先建立一个大根堆

假设从n/2开始筛选,此后每次减1,如图从819开始,筛选两个左右孩子最大的一个与之交换,交换完毕后检查交换后关键字是否满足大根堆的性质,不满足,继续重复操作,直到建立好一个大根堆

第二步:交换堆顶与堆底元素

第三步:除去堆底的元素剩余元素重复第一步的操作,且从堆顶元素开始

如图:   

      堆顶948与堆底435交换,除去948剩下重复第一步操作

4.代码实现

以带哨兵为例(带哨兵指第一个元素用来存储关键字),以下代码有解释

//基于大根堆(带哨兵)排序,
void headSort(int arr[],int length) {
   
    bulidMaxHeap(arr, length);   //首次建大根堆
    for (int i = length; i > 1; i--) {    //n-1趟交换和建堆
        
        swap(arr, 1, i);   //将堆顶与堆底元素交换
        adjustDown(arr, 1, i-1);  //剩余元素继续调整
    }
}
void swap(int arr[], int i, int j) {
    int temp;
    temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
void bulidMaxHeap(int arr[], int length) {
    for (int i = length / 2; i >0; i--) {  //从最大的 i<length/2 开始一次往前调整
        adjustDown(arr, i, length);
    }
}
void adjustDown(int arr[], int key, int length) {
    
    arr[0] = arr[key];     //arr[0]做哨兵 ,暂时存储关键字元素防止被覆盖
    int i;
    for ( i = 2 * key; i <=length; i=i*2)    //从左孩子开始扫描
    {
        if (i < length   && arr[i] < arr[i + 1]) {  //取左右孩子的最大值,( i<length 防止左孩子没有右兄弟 )
            ++i;
        }
        if (arr[0] >= arr[i]) {  //判断关键字是否大于左右孩子中最大的那一个
            break;
        }
        else {
            arr[key] = arr[i];  //将左右孩子最大的赋值给关键字
            key = i;        //调整指针位置,接下来继续循环,判断调整后的元素是否符合堆
        }
        
    }
    arr[key] = arr[0];   //将关键字放在最终位置
}
int main()
{
    int arr[] = { 0,11,10,2,9,8,7,1,4,5,6  };
    int length = sizeof(arr) / sizeof(arr[0]);
    headSort(arr, length-1);
    for (int i = 1; i < length; i++)
    {
        printf("%d\n", arr[i]);
       
    }
}


排序后:

水平有限,如有不正,请指点

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值