堆排序算法原理及实现

article/2025/10/29 15:41:11

堆排序是排序中一种比较重要的算法,和快速排序一样,其复杂度也是O(nlogn);同时也是一种原地排序算法:在任何时候,数组中只有常数个元素存储在输入数组以外。堆这种数据结构是处理海量数据比较常见的结构,海量数据的TOP K问题一般可以通过分治法+hash+堆这种数据结构解决。值得注意的是,这里将的“堆”准确的说是二叉堆,逻辑上是一棵类似完全二叉树的数据结构。与内存管理中提到的“堆”是两个不同的概念,后者堆内存类似链表的数据结构。下面首先介绍堆数据结构特性,然后将如何建立最大堆或最小堆,最后再讲解堆排序原理及其实现。

    二叉堆数据结构在物理存储上一般表示为一种数组对象,该数组中的数据按照其逻辑结构树的广度优先算法(队列优先)来存储对应的值。树中的每个结点与数组中存放该结点值的那个元素对应。树的每一层都是填满的,最后一层可能除外(最后一层从一个结点的左子树开始填)。表示堆的数组A是一个具有两个属性的对象: length[A]是数组中元素个数, heap-size[A]是存放在A中的堆的元素个数,就是说,虽然A[0...length[A]-1]中都可以包含有效值,但A[heap-size[A] - 1]之后的元素都不属于相应的堆,此处heap-size[A] <= length[A]。接下来树的根均从A[0]开始,这里与C语言数组索引下标保持一致,索引从0开始计算。给定某个结点的下标i,其父结点PARENT(i),左儿子LEFT(i)和右儿子RIGHT(i)的下标可以简单计算出来:

#define PARENT(i)    i/2

#define LEFT(i)     2*i + 1

#define RIGHT(i)   2*i + 2

一个最大堆(大根堆)可以被看做一棵二叉树和一个数组。如上图所示,逻辑结构为二叉树,物理存储一般为数组。圆圈内的数字表示树中每个结点存储的值,结点上方的数字表示对应的数组下标。数组上下的连线表示父子关系,且父结点总在子结点的左边。图中这棵树的高度为3,存储值为8的左右孩子分别为2与4。

堆分为大根堆与小根堆。在这两种堆中,结点内数值要满足堆特性,其细节则视堆的种类而定。在最大堆中,最大堆特性指的是除了根结点之外的每个结点i,其父结点不小于其左右孩子结点。最小堆则相反,最小堆特性是指除了根以外的每个结点i,其父结点不大于其左右孩子结点。因此在最大堆中,根元素为最大值,而最小堆中,根元素为最小值。

    堆可以看做一颗树,结点在堆中的高度定义为从本结点到叶子的最长简单下降路径上边的数目;定义堆的高度为树根的高度。具有n个元素的堆是基于一棵完全二叉树的,因而其高度为O(lgn)。我们将看到,堆结构上的一些基本操作的运行时间至多与树的高度成正比,为O(lgn)。

如何保持堆的特性?

MAX-HEAPIFY是对最大堆进行操作的重要的子程序。其输入为一个数组A和下标i,当MAX-HEAPIFY被调用时,我们假定以LFEF(i)和RIGHT(i)为根的两棵二叉树都是最大堆,但这时A[i]可能小于其子女,这样就违反了最大堆性质。MAX-HEAPIFY让A[i]在最大堆中“下降”,使以i为根的子树成为最大堆。

MAX-HEAPIFY(A, i)

1  l <------ LEFT(i)

2  r <------ RIGHT(i)

3  if l <= heap-size[A] and A[l] > A[i]

4      then  largest <------ l

5      else largest  <------ i

6  if r <= heap-size[A] and A[r] > A[largest]

7      then largest <------ r

8  if largest != i

9      then exchange A[i] <------> A[largest]

10          MAX-HEAPIFY(A, largest)



MAX-HEAPIFY实际上每一步在父结点和其孩子结点中寻找最大值作为最大堆根结点, 即在算法每步中,从元素A[i],A[LEFT(i)] 和 A[RIGHT(i)]中找出最大的,并将其下标存在largest中,如果A[i]是最大的,则以i为根的子树已是最大堆,程序结束。否则,i的某个子结点中有最大元素,则交换A[i]和A[largest],从而使i及其子女满足堆性质。下标为largest的结点在交换后的值是A[i],以该结点为根的子树又有可能违反最大堆性质。因而要对该子树递归调用MAX-HEAPIFY。

当heap-size[A] = 10 时,MAX-HEAPIFY(A,1)(索引从0开始)的作用过程如上图所示,(a)为初始构造最大堆,在结点i=1处A[1]违反了最大堆性质,因为它不大于它的两个子女。在(b)中通过交换A[1]与A[3],在结点1处恢复了最大堆性质,但又在结点3处违反了最大堆性质。现在递归调用MAX-HEAPIFY(A, 3),置i=3, (c)中交换了A[3与A[8],结点3的最大堆性质得到恢复,递归调用MAX-HEAPIFY(A, 8)对该数据结构不会再引起任何变化。

当MAX-HEAPIFY作用在一棵以结点i为根的、大小为n的子树上时,其运行时间为调整元素A[i]、A[LEFT(i)]和A[RIGHT(i)]的关系时所用时间O(1),再加上对以i的某个子结点为根的子树递归调用MAX-HEAPIFY所需时间。i结点的子树大小至多为2n/3(最坏情况发生在最底层恰好半满的时候),那么MAX-HEAPIFY的运行时间可由下式描述: T(n) <= T(2n/3) + O(1),该递归式的解为T(n) = O(lgn)。或者说,MAX-HEAPIFY作用一个高度为h的结点所需的运行时间为O(h)。


如何建堆?

我们可由自底向上地用MAX-HEAPIFY来将一个数组A[0...n-1](此处n=length[A])变成一个最大堆。子数组A[n/2...n-1]中的元素都是树中的叶子,因此每个都可看作是只含一个元素的堆。过程BUILD-MAX-HEAP对树中的每一个其他结点都调用一次MAX-HEAPIFY。

BUILD-MAX-HEAP(A)

1    heap-size[A] <------ length[A]

2    for i <------ length[A]/2 - 1 downto 0

3            do MAX-HEAPIFY(A, i)

堆排序算法

开始时,堆排序算法先用BUILD-MAX-HEAP将输入数组A[0...n-1](此处n=length[A])构成一个最大堆。因为数组中最大元素在根A[0],则可以通过它与A[n-1]互换来达到最终正确的位置。现在,如果从堆中“去掉”结点n-1(通过减小heap-size[A]),可以很容易地将A[1...n-2]建成最大堆。原来根的子女仍然是最大堆,而新的根元素可能违背了最大堆性质。这时调用MAX-HEAPIFY(A, 0)就可以保持这一性质,在A[0...(n-2)]中构造出最大堆。堆排序算法不断重复这个过程,堆的大小由n-1一直降到2。

HEAPSORT(A)

1    BUILD-MAX-HEAP(A)

2    for i <------ length[A] - 1 downto 1

3            do exchange A[0] <------> A[i]

4                heap-size[A] <------ heap-size[A] - 1

5                MAX-HEAPIFY(A, 0)

HEAPSORT过程的时间代价为O(nlgn)。其中调用BUILD-MAX-HEAP的时间为O(n), n-1次HEAP-MAX-HEAPIFY调用中每一次的时间代价为O(lgn)。下面我们给出了初始化最大堆建立之后堆排序的一个例子。图中每个最大堆与算法第2~5行的for循环的每次迭代的开始对应。

 

        研究完了堆排序算法原理及算法流程,下面主要来用C代码实现,既然伪码都有了,那么实现起来就easy了!下面是堆排序算法的C代码实现过程(这里,我们随机产生15个整数,然后对其进行排序,验证其算法的准确性):

// Name        :堆排序算法
// Author      : @CodingGeek
// Version     : 1.0
// Copyright   : Your copyright notice
// Description : 堆排序算法C代码实现
//============================================================================#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>#define PARENT(i) (i)/2
#define LEFT(i) 2 * (i) + 1
#define RIGHT(i) 2 * (i + 1)void swap(int &a, int &b)
{int temp = a;a = b;b = temp;
}void max_heapify(int arr[], int len, int index)
{int left = LEFT(index);int right = RIGHT(index);int largest = index;if (left < len && arr[left] > arr[index]){largest = left;}if (right < len && arr[right] > arr[largest]){largest = right;}if (largest != index){swap(arr[largest], arr[index]);max_heapify(arr, len, largest);}
}void build_max_heap(int arr[], int len)
{int i = 0;if (len <= 1){return;}for (i = len/2 - 1; i >= 0; i--){max_heapify(arr, len, i);}}void heap_sort(int arr[], int len)
{int i = 0;if (arr == NULL || len <= 1){return;}build_max_heap(arr, len);for (i = len - 1; i >= 1; --i){swap(arr[0], arr[i]);max_heapify(arr, --len, 0);}
}int main(void)
{const int arr_num = 15;int *pstArr =  new int[arr_num];srand(time(NULL));for (int i = 0; i < arr_num; i++){pstArr[i] = rand() % 100;printf("%d ", pstArr[i]);}printf("*************\n");heap_sort(pstArr, arr_num);for (int i = 0; i < arr_num; i++){printf("%d ", pstArr[i]);}delete []pstArr;return 0;
}

运行程序,我们可以得出堆排序算法之前与之后的打印结果:





http://chatgpt.dhexx.cn/article/MOVep6Ia.shtml

相关文章

堆排序算法Java

基本原理 1):将带排序的序列构造成一个大顶堆&#xff0c;根据大顶堆的性质&#xff0c;当前堆的根节点&#xff08;堆顶&#xff09;就是序列中最大的元素 2):将堆顶元素和最后一个元素交换&#xff0c;然后将剩下的节点重新构造成一个大顶堆&#xff1b; 3):重复步骤2 小知识…

堆排序算法详细分析

一、堆相关概念 1.堆 堆是完全二叉树&#xff0c;即除最后一层外&#xff0c;其它层都是满的&#xff0c;且最后一层从左到右依次都有元素。如下图所示。 堆是用数组来实现的&#xff0c;图中下标就为数组的下标&#xff0c;其对应数组[5, 1, 7, 2, 8, 6, 3, 9, 4]&#xf…

数据结构——堆排序(算法)

基本介绍 1&#xff09;、堆排序是利用堆这种数据结构而设计的一种排序算法&#xff0c;堆排序是一种选择排序&#xff0c;它的最好、最坏、平均时间复杂度均为O(nlogn)&#xff0c;它也是不稳定排序。2&#xff09;、堆是具有以下性质的完全二叉树&#xff1a;每个节点的值都…

C++:堆排序算法详解

图解排序算法(三)之堆排序 预备知识 堆排序 堆排序是利用堆这种数据结构而设计的一种排序算法&#xff0c;堆排序是一种选择排序&#xff0c;它的最坏&#xff0c;最好&#xff0c;平均时间复杂度均为O(nlogn)&#xff0c;它也是不稳定排序。首先简单了解下堆结构。 堆 堆是具有…

排序算法:堆排序算法实现及分析

堆排序介绍 堆排序&#xff08;Heap Sort&#xff09;就来利用堆&#xff08;假设利用大顶堆&#xff09;进行排序的方法。它的基本思想是&#xff0c;将待排序的序列构成一个大顶堆。此时&#xff0c;整个序列的最大值就是堆顶的根结点。将它移走&#xff08;其实就是将其与堆…

堆排序算法 总结

最近面试&#xff0c;老是被问到堆排序算法。 回答时老是感觉思路不清楚&#xff0c;现在总结一下&#xff0c;把思路弄清楚的。 1.堆排序是利用堆的特性对记录序列进行排序的一种排序方法。 好的那么堆得特性是什么呢&#xff1f; 堆得定义&#xff1a; 堆是满足下列性质的数…

Java实现堆排序算法

堆排序是计算机编程中一种流行且高效的排序算法。学习如何编写堆排序算法需要了解两种类型的数据结构-数组和树。 我们要排序的初始数字集存储在数组中&#xff0c;例如[10, 3, 76, 34, 23, 32]&#xff0c;排序后&#xff0c;我们得到一个排序后的数组[3,10,23,32,34,76] 堆排…

精通八大排序算法系列:二、堆排序算法

精通八大排序算法系列&#xff1a;二、堆排序算法 作者:July 、二零一一年二月二十日本文参考&#xff1a;Introduction To Algorithms&#xff0c;second edition。------------------- 此精通排序算法系列&#xff0c;前一节&#xff0c;已讲过了一、快速排序算法&#xff0c…

堆排序算法详解

一、堆排序算法原理和动态图解 将待排序的序列构造成一个大顶堆。此时&#xff0c;整个序列的最大值就是堆顶的根节点。将它移走(其实就是将其与堆数组的末尾元素交换&#xff0c;此时末尾元素就是最大值)&#xff0c;然后将剩余的n-1个序列重新构造成一个堆&#xff0c;这样就…

【堆排序算法】(C语言实现)

堆排序 一.堆排序1.堆的概念及性质1.1堆的概念1.2堆的性质 二.向下调整和向上调整两大算法1. 向下调整算法2.1 向下调整算法基本思路 2. 向上调整算法2.2 向上调整的基本思路 三.堆的实现&#xff08;小堆&#xff09;1.头文件包含2. 接口实现 四.任意树调整为堆&#xff08;小…

堆排序算法

堆排序 堆排序&#xff08;Heapsort&#xff09;是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构&#xff0c;并同时满足堆积的性质&#xff1a;即子结点的键值或索引总是小于&#xff08;或者大于&#xff09;它的父节点。 基本思想 利用大顶堆…

3.图解排序算法(三)之堆排序

作者&#xff1a; dreamcatcher-cx 出处&#xff1a; http://www.cnblogs.com/chengxiao/ 本文版权归作者和博客园共有&#xff0c;欢迎转载&#xff0c;但未经作者同意必须保留此段声明&#xff0c;且在页面明显位置给出原文链接。 原文链接&#xff1a;https://www.cnblogs.…

排序算法:堆排序

相关博客&#xff1a; 排序算法&#xff1a;冒泡排序、插入排序、选择排序、希尔排序 排序算法&#xff1a;归并排序、快速排序 排序算法&#xff1a;桶排序、计数排序、基数排序 排序算法&#xff1a;堆排序 十大排序算法小结 一、堆&#xff1a; 1、什么是堆&#xff1…

排序算法 —— 堆排序(图文超详细)

文章目录 堆排序1. 排序思路2. 如何建成大根堆2.1. 将待排序的数据看成一个完全二叉树。2.2. 建堆思想2.3. 建堆步骤2.3.1.先将最后一棵子树建成大根堆2.3.2.将其余子树建成大根堆2.3.3. 代码分析 3 如何实现堆排序3.1 排序思路3.2 代码实现的思路3.3 代码分析3.4 排序过程 4. …

十大经典排序算法----堆排序(超详细)

目录 1. 堆排序的基础知识 1.1 大顶堆&&小顶堆 1.2 向下调整算法 1.3 物理结构与逻辑结构的关系 2. 堆排序详解 2.1 堆排序整体思路 2.2 思路详解 2.2.1 建堆 2.2.2 大堆or小堆 2.2.3 输出数据 3. 时间复杂度分析 4. 完整代码 5. 彩蛋 1. 堆排序的基础知识…

【转载】堆排序算法(图解详细流程)

堆排序的时间复杂度O(N*logN),额外空间复杂度O(1)&#xff0c;是一个不稳定性的排序 目录 一 准备知识 1.1 大根堆和小根堆 二 堆排序基本步骤 2.1 构造堆 2.2 固定最大值再构造堆 三 总结 四 代码 一 准备知识 堆的结构可以分为大根堆和小根堆&#xff0c;是一个完全…

堆排序算法(图解详细流程)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 堆排序基本介绍大顶堆举例说明小顶堆举例说明堆排序的基本思想堆排序步骤图解说明堆排序的基本思路总结 堆排序基本介绍 堆排序是利用堆这种数据结构而设计的一种排…

堆排序详细图解(通俗易懂)

什么是堆 堆是一种叫做完全二叉树的数据结构&#xff0c;可以分为大根堆&#xff0c;小根堆&#xff0c;而堆排序就是基于这种结构而产生的一种程序算法。 堆的分类 大根堆:每个节点的值都大于或者等于他的左右孩子节点的值 小根堆:每个结点的值都小于或等于其左孩子和右孩子…

PreferenceActivity(二)

看到很多书中都没有对PreferenceActivity做介绍&#xff0c;而我正好又在项目中用到&#xff0c;所以就把自己的使用的在这总结一下&#xff0c;也方便日后查找。 PerferenceActivity是什么&#xff0c;看下面的截图&#xff1a; Android系统截图&#xff08;左&#xff09; …

android设置页面之PreferenceActivity及Preference

离上篇博客刚好一周&#xff0c;希望后面会记录更多的内容&#xff0c;也算自己的Android笔记吧。 本篇主要记录一般的android设置页面PreferenceActivity的使用以及与之剪不断&#xff0c;理还乱的Preference。 &#xff08;一&#xff09;如何使用 Android系统自带的设置应用…