堆排序

#include "util.h"

//堆排序
//
//二叉堆的特性就是每个节点的数值都比子节点的数据大,所以二叉堆最顶端的数值是最大的
//这里采用数组来存储二叉堆,为了方便计算父节点与叶子节点的索引,数据索引0没有使用,数
//组有效索引从1开始, 给定任意节点索引X,该节点的父节点索引为X/2,该节点的左叶子节点索
//引为 X*2, 右叶子节点索引为X*2+1
//
//       1
//   ---------
//   |       |
//   2       3
// -----   -----
// |   |   |   |
// 4   5   6   7
//
// array |0|1|2|3|4|5|6|7|
//
//堆排序的核心思想就是基于二叉堆的数据结构进行排序算法实现
//1 首先将数据构造成二叉堆
//2 之后将二叉堆顶端最大值与数组末尾的值进行交换,缩小数组长度,使得最大值停留在
//当前数组的末尾.
//3 当之前数据末尾数据移到二叉堆顶端后,二叉堆可能失去了原来的性质,此时需要从二
//叉堆顶部重新进行下沉处理,使得数组剩余数据再次构成二叉堆特性.
//4 重复步骤2~3,省得从二叉堆中依次取出最大值,并倒序放置到数组中,最终形成降序排序
void sort(int iArray[], int count)
{
    int k = 0;

    //将原数组数据构造成二叉堆,从数组最后索引开始进行下沉处理来构造二叉堆,
    //之后数组索引不断进行递减,使得最终从顶端索引1进行下沉处理来完成最终的二叉
    //堆构造
    //k = count / 2,这里是为了跳过叶子节点,如上图示例,叶子节点4\5\6\7下面已经没
    //有子节点了,所以不需要进行下沉处理.
    //k >= 1,是因为当前采用数组方式存储二叉堆,为了计算父子索引方便,数组索引0不
    //使用,直接从索引1开始.
    for ( k = count / 2; k >= 1; k-- )
    {
        sink(iArray, count, k); 
    }

    //开始进行堆排序处理
    //count > 2,是因为当前不使用数组0索引,所以如果二叉堆中至少需要2个数据进行
    //排序处理,则至少数组长度应该大于2
    while ( count > 2 )
    {
        count--;
        //将二叉堆顶部索引1的数值与当前数组末尾的值进行交换
        exch(iArray, count, 1);
        //数组末尾的值交换到二叉堆顶端后,从顶端开始进行下沉处理,保证二叉堆的
        //特性有效
        sink(iArray, count, 1);
    }
}

//二叉堆的特性就是每个节点的数值都大于子节点的数值
//当新加或删除节点后,就要重新进行处理,来保证二叉堆的特性不变,这
//里的算法是属于下降方式处理,其思想如下:
//1 首先判断当前节点下还有子节点
//2 从左叶子节点及右叶子节点中选择一个最大的值
//3 使用该值与当前节点进行比较,因为当前节点是叶子节点的父节点,所以
//  按二叉堆的特性,该节点应该不能小于叶子节点,如果该节点大于叶子节
//  点,则满足二叉堆特性,退出处理,否则需要将此节点与最大的叶子节点
//  值进行交换
//4 如果在第3步中发生了交换,则当前新值已经移到了最大叶子节点的位置
//  ,此时需要继续检查当前位置的值是否大于当前索引下的叶子节点,处理
//  方法同第1~3步相同
void sink(int iArray[], int count, int k)
{
    //至少存在左叶子节点
    while ( 2 * k < count )
    {
        //左叶子节点索引值
        int leaf = 2 * k;

        //如果存在右叶子节点, 则从两个叶子节点中挑出一个
        //最大值的叶子索引
        if ( leaf + 1 < count )
        {
            if ( less(iArray[leaf],  iArray[leaf + 1]) )
            {
                leaf++;
            }
        }

        //如果当前父节点大于等于最大的叶子节点,则已经满足二叉堆
        //特性,退出处理
        if ( !less(iArray[k], iArray[leaf]) ) 
        {
            break;
        }

        //当前父节点小于叶子节点,则进行父节点和叶子节点的数据交换
        exch(iArray, k, leaf);

        //当前待处理的数据已经移到了最大的叶子节点位置,此时需要将
        //当前索引更改为替换后的叶子索引位置,用于继续当前数据
        //与下一叶子值的比较处理
        k = leaf; 
    }
}

int main()
{
    int iArray[] = {-1, 2, 5, 6, 1, 8, 3, 9, 4, 3, 1, 7};

    sort(iArray, 12);
    arrayShow(iArray, 12);

    return 0;
}


#include <stdio.h>
#include "util.h"

void arrayShow(int iArray[], int size)
{
    int i = 0;

    for ( i = 0; i < size; i++ )
    {
        printf("%d ", iArray[i]);
    }
    printf("\r\n");
}

int less(int a, int b)
{
    if ( a < b ) return 1;
    return 0;
}

int compare(int a, int b)
{
    if ( a > b ) return 1;
    if ( a < b ) return -1;
    return 0;
}

void exch(int iArray[], int indexA, int indexB)
{
    int tmp = iArray[indexA];
    iArray[indexA] = iArray[indexB];
    iArray[indexB] = tmp;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值