常用排序算法递归篇之堆排序

      在讲堆排序之前,我们先来了解一下什么是堆,堆的定义如下:

      n个关键字序列array[0...n-1]称为(Heap),当且仅当该序列满足如下性质:

      array[i] <= array[2i + 1] && array[i] <= array[2i + 2] (1 ≤ i ≤ n),当然,这是小顶堆,大顶堆则换成>=号。array[i]相当于二叉树的非叶结点,array[2i + 1]则是左孩子,array[2i + 2]是右孩子。

      若将此序列array[0...n-1]看做是一棵完全二叉树,则堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。

      用大顶堆排序的基本思想:

      1、先将初始序列array[0...n-1]建成一个大顶堆,此堆为初始的无序区

      2、再将关键字最大的记录array[0](即堆顶)和无序区的最后一个记录array[n-1]交换,由此得到新的无序区array[0...n-2]和有序区array[n-1],且满足arrya[0...n-2].keys ≤ array[n-1].key

      3、由于交换后新的根array[0]可能违反堆性质,故应将当前无序区array[0...n-2]调整为堆。然后再次将array[0...n-2]中关键字最大的记录array[0]和该区间的最后一个记录array[n-2]交换,由此得到新的无序区array[0...n-3]和有序区array[n-2...n-1],且仍满足关系array[0...n-3].keys ≤ array[n-2...n-1].keys,同样要将array[0...n-3]调整为堆。

      ……

      直到无序区只有一个元素为止。

      堆排序的具体实现代码如下:

/*****************************************************************************
 函 数 名  : heap_sort
 功能描述  : 堆排序 
 输入参数  : array  待调整的堆数组
             length 数组的长度
 输出参数  : 无
 返 回 值  : 无 
 修改历史      :
  1.日    期   : 2012/08/23
    作    者   : liguangting
    修改内容   : 

*****************************************************************************/
void heap_sort(int *array, int length)
{
    int i = 0;
    
    if (NULL == array || 0 == length)
    {
        return;
    }
    
    //构造大顶堆 
    for (i = length / 2 - 1; i >= 0; i--)
    {
        heap_adjust(array, i, length);
    }
    
    //从最后一个元素开始对数组进行调整,不断缩小调整的范围直到第一个元素 
    for (i = length - 1; i > 0; i--)
    {
        //交换第一个元素和当前的最后一个元素,保证当前的最后一个元素在当前数组中是最大的 
        swap(array[0], array[i]);
        
        //调整完后的第一个元素是当前数组的最大元素 
        heap_adjust(array, 0, i);
    }
}

/*****************************************************************************
 函 数 名  : heap_adjust
 功能描述  : 根据数组构建大顶堆 
 输入参数  : array  待调整的堆数组
             index  待调整的数组元素的位置
             length 数组的长度
 输出参数  : 无
 返 回 值  : 无 
 修改历史      :
  1.日    期   : 2012/08/23
    作    者   : liguangting
    修改内容   : 

*****************************************************************************/
void heap_adjust(int *array, int index, int length)
{
    int child;
    int temp = array[index];
    
    if (2 * index + 1 >= length)
    {
        return;
    }

    //子结点位置 = 2 * 父结点位置 + 1
    child = 2 * index + 1;
        
    //得到子结点中较大的结点 
    if (child < length - 1 && array[child + 1] > array[child])
    {
        ++child;
    }
            
    //如果较大的子结点大于父结点那么把较大的子结点往上移动,替换它的父结点 
    if (temp < array[child])
    {
        array[index] = array[child];
    }
    else
    {
        return;
    }
        
    //最后把需要调整的元素值放到合适的位置 
    array[child] = temp;
    
    heap_adjust(array, child, length);
}


      上面的堆调整用了递归的思路,其实也不难看出,我们可以用非递归的方法来完成一轮堆调整,可以参考如下实现:

/*****************************************************************************
 函 数 名  : heap_adjust
 功能描述  : 根据数组构建大顶堆 
 输入参数  : array  待调整的堆数组
             index  待调整的数组元素的位置
             length 数组的长度
 输出参数  : 无
 返 回 值  : 无 
 修改历史      :
  1.日    期   : 2012/08/23
    作    者   : liguangting
    修改内容   : 

*****************************************************************************/
void heap_adjust(int *array, int index, int length)
{
    int child;
    int temp = array[index];
    
    for (; 2 * index + 1 < length; index = child)
    {
        //子结点位置 = 2 * 父结点位置 + 1
        child = 2 * index + 1;
        
        //得到子结点中较大的结点 
        if (child < length - 1 && array[child + 1] > array[child])
        {
            ++child;
        }
            
        //如果较大的子结点大于父结点那么把较大的子结点往上移动,替换它的父结点 
        if (temp < array[child])
        {
            array[index] = array[child];
        }
        else
        {
            break;
        }
        
        //最后把需要调整的元素值放到合适的位置 
        array[child] = temp;
    }
}


      堆排序的每一趟排序结果都是选出无序区的最大值或最小值,跟选择排序的思路是一致的。但是在处理过程中却截然不同,那堆排序与选择排序有什么不同点呢?

      直接选择排序中,为了从array[0...n-1]中选出关键字最小的记录,必须进行n-1次比较,然后在array[1...n-1]中选出关键字最小的记录,又需要做n-2次比较。事实上,后面的n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,但由于前一趟排序时未保留这些比较结果,所以后一趟排序时又重复执行了这些比较操作。堆排序可通过树形结构保存部分比较结果,可减少比较次数。

      至此,常用排序算法系列的文章结束,有什么问题欢迎指正和多多交流。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
堆排序是一种基于二叉堆的排序算法。首先,我们需要了解什么是二叉堆。二叉堆是一种完全二叉树,它可以分为两种类型:最大堆和最小堆。在最大堆中,父节点的值大于等于其子节点的值,而在最小堆中,父节点的值小于等于其子节点的值。 堆排序的步骤如下: 1. 首先,我们需要根据给定的数据构建一个堆。这可以通过将数据插入到一个空堆中,或者通过使用自底向上的方式进行堆化操作来完成。 2. 一旦堆被构建好了,我们可以通过将根节点与最后一个叶子节点交换,并移除最后一个节点来获取最大(或最小)值。这样,我们就将最大(或最小)值移到了正确的位置。 3. 接下来,我们需要重新调整剩余的节点,使其满足堆的性质。这可以通过对交换后的根节点进行递归调整来完成。 4. 重复步骤2和步骤3,直到所有的节点都被移除并按照正确的顺序排列。 在引用和引用中提供的代码中,可以看到两种不同的实现堆排序的方法。其中引用中的代码实现了冒泡排序算法,而引用列出了堆排序的多种实现方式。在引用的代码中,冒泡排序算法被用作堆排序的一部分。 在引用中的代码中,可以看到使用指针来实现的快速排序算法。这不是堆排序算法,而是另一种常见的排序算法。 因此,从提供的引用中并没有直接提供形成小堆的具体步骤。如果您想了解如何实现小堆,您可以搜索相关的资料或参考其他可靠的来源。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [六大排序算法:插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序](https://blog.csdn.net/weixin_50886514/article/details/119045154)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值