堆排序

一、堆
将一个数组arr = {12,23,25,17,48,49,56,65,73,89},也就是一个无序序列,看作是一个完全二叉树,则对应以下结构:

这里写图片描述

如果该结构是是的话,则堆顶元素(二叉树的根)必须为序列中的最大值或最小值,因而堆又分为大根堆和小根堆。

1.大根堆:整个堆中,左子结点的数据和右子结点的数据都比父结点的数据小。

这里写图片描述

2.小根堆:整个堆中,左子节点的数据和右子节点的数据都比父结点的大。

这里写图片描述

二、堆排序

先补充几个概念:
叶子结点:没有子节点的结点。
非叶子结点:至少有一个左子节点的结点。
已知父结点的位置为n,则左子节点的位置为2*n+1,右子节点的位置为2*(n+1)。
已知左子节点或右子节点的位置为n,父结点的位置则为(n-1)/2。

这里写图片描述

排序思想(升序):将一个含有n个数的无序序列调整为一个大根堆,利用大根堆的特性——根节点的数据为整个序列中最大的数据,得到最大数据之后,放入最后一个叶子结点中(置后),再对剩下的(n-1)个数据进行调整,再建一次大根堆,由此循环,直到得到一个递增序列。其中,最重要的部分就是调整为大根堆的具体方法

1.将无序序列调整为一个大根堆

从最后一个树开始进行调整,对其中的左、右子节点的下标进行标记,记录较大值的下标,判断是否要和树中的父结点交换数据,再对改变了的左子节点或右子节点下的树进行调整(如果有的话)。
总之就是:从后往前多次调整,每次调整都是从上至下

还是拿上面的arr = {12,23,25,17,48,49,56,65,73,89}举例,开始第一次建大根堆。

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

最后得到一个大根堆:
这里写图片描述

2.将最大值后置,再进行调整

第一次建成大根堆后,堆顶元素即为该序列中的最大值,因为我们要得到的是一个递增序列,所以将堆顶元素与最后一个叶子结点交换,再让剩下的(n-1)个数据进行调整,再建大根堆,不断地筛选出最大值,再置后,再建堆调整,直到得到一个完整的递增序列。

这里写图片描述

这里写图片描述

又得到剩下元素中的最大值,后置:

这里写图片描述

这里写图片描述

重复以上步骤,得:

这里写图片描述

这里写图片描述

调整完毕,得到递增序列:

这里写图片描述

即arr = {12,17,23,25,48,49,56,65,73,89}.

实现代码见下:

void Adjust(int *arr, int i, int pos)
{
    int maxnode;//记录左、右结点中最大值的下标
    for(maxnode=i*2+1; maxnode<=pos; maxnode=maxnode*2+1)
    {
        if(maxnode<pos && arr[maxnode]<arr[maxnode+1])//右结点存在,且比左结点的数据大,maxnode交换
        {
            maxnode++;
        }
        if(arr[maxnode] <= arr[i])//父结点的值大于左结点的值,跳出,不需要调整
        {
            break;
        }

        int tmp = arr[maxnode];//与父结点交换数据
        arr[maxnode] = arr[i];
        arr[i] = tmp;
        i = maxnode;
    }
}

void HeapSort(int *arr, int len)//递增排序
{
    int index = len-1;//index表示元素下标

    for(int i=(index-1)/2; i>=0; i--)//先把无序序列调整为大根堆,从最后一个非叶子结点开始
    {
        Adjust(arr, i, index);
    }

    for(int j=index; j>=0; j--)//开始堆排序
    {
        int tmp = arr[j];             //1.将父结点的数据交换到最后一个叶子结点
        arr[j] = arr[0];               //2.交换之后,再对前i-1个元素进行调整->大根堆
        arr[0] = tmp;

        Adjust(arr, 0, j-1);
    }
}

void Show(int *arr, int len)
{
    for(int i=0; i<len; i++)
    {
        printf("%d ",arr[i]);
    }
}


int main()
{
    int arr[6] = {2,25,24,89,53,62};
    int len = sizeof(arr)/sizeof(arr[0]);
    Show(arr,len);

    printf("\n");
    HeapSort(arr,len);
    Show(arr,len);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值