数据结构堆及堆排序详解

Posted Kant101

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构堆及堆排序详解相关的知识,希望对你有一定的参考价值。

1. 简介

堆(Heap)在计算机科学中可以表示内存的一部分,也是数据结构与算法中一种非常常用的数据结构。

尤其是在较大数据量的排序等问题中,堆排序经常被使用,因为堆排序的时间复杂度为 O(nlogn)堆排序也是面试中经常被问到的对大量数据排序的算法,如海量数据选出 top K 等。

堆的结构
是一棵完全二叉树,且分为 大顶堆小顶堆 两种结构:大顶堆中根结点的值大于左右子结点的值;小顶堆中根结点的值小于左右子结点的值。

大顶堆示例如下图所示:

2. 建堆

我们以如下所示的原始完全二叉树为例,详细描述大顶堆建堆的整过程。

给定的数字的初始序列为: 85、55、82、57、68、92、99、98、66、56
将它们按照树的层序从上到下、从左到右依次摆放就得到如上图所示的初始的完全二叉树(堆是一种完全二叉树)

现在要调整这个初始堆,使它成为一个大顶堆
调整的策略是:从最后一个结点开始从右向左、从下到上调增,使得父结点的值大于两个子结点的值。并且,对交换的子结点还要进行递归,使得它下面的子树仍然满足大顶堆的性质。

1)56比它的父结点68的值下,因此56不需要调整;

2)66和98两个子结点的值和它们的父结点相比较,选择最大的值做父结点,将原来的父结点的值与最大值的根结点交换,如下图:

3)99和92与它们的父结点相比较,选择最大的做父结点,得到如下图

4)68和98与它们的父结点相比较,选择最大的98做父结点,即55和98交换。交换后还要对55进行递归,55比它的子结点小,继续调整,最终得到如下图:

5)99和98与它们的父结点相比较,选择最大的99做父结点,即将85与99交换。交换后还需要对85递归,最终得到如下图:

最终,所有结点都进行了调整,得到了大顶堆如下:

代码

const int maxn = 100;
//heap为堆,n为元素个数,heap数组有效数字下标要从1开始
int heap[maxn], n = 10;

/**
 * 对 heap 数组在 [low, high] 范围进行向下调整
 * 其中low为欲调整结点的下标,high一般为堆的最后一个元素的数组下标
 */
void downAdjust(int low, int high) 
    int i = low, j = i * 2; // i为欲调整结点,j为其左孩子
    while (j <= high)  //存在孩子结点
        // 如果右孩子存在,且右孩子的值大于左孩子
        if (j + 1 <= high && heap[j + 1] > heap[j]) 
            j = j + 1; // 让j存储右孩子下标
        
        // 如果孩子中最大的权值比欲调整结点i大
        if (heap[j] > heap[i]) 
            swap(heap[j], heap[i]); // 交换最大权值的孩子与欲调整结点i
            i = j; //保持i为欲调整结点,j为其左孩子
            j = i * 2;
         else 
            break; // 孩子的权值均比欲调整结点i小,调整结束
        
    

3. 堆排序

4. 测试

参考文献

以上是关于数据结构堆及堆排序详解的主要内容,如果未能解决你的问题,请参考以下文章

用仿函数实现大小堆及堆排序

堆及堆的变种

挖掘算法中的数据结构:排序算法总结 和 索引堆及优化(堆结构)

堆结构及堆排序详解

堆排序详解

最小/大堆的操作及堆排序