Java实现堆排序算法

article/2025/10/29 18:18:37

堆排序是计算机编程中一种流行且高效的排序算法。学习如何编写堆排序算法需要了解两种类型的数据结构-数组和树。

我们要排序的初始数字集存储在数组中,例如[10, 3, 76, 34, 23, 32],排序后,我们得到一个排序后的数组[3,10,23,32,34,76]

堆排序的工作原理是将数组的元素可视化为一种特殊的完整二叉树,称为堆。

前提条件是,您必须了解完整的二叉树和堆数据结构。


数组索引和树元素之间的关系

完整的二叉树具有一个有趣的属性,我们可以用来查找任何节点的子代和父代。

如果数组中任何元素的索引为 一世,索引中的元素2i+1将成为左子元素,2i+2索引中的元素将成为右子元素。另外,索引处任何元素的父元素由(i-1)/2的下限给出。

左边是一个图,右边是同一图的数组表示形式,用于比较等效索引

数组和堆索引之间的关系

让我们测试一下

1的左孩子(索引0)
= 索引(2 * 0 + 1)中的元素 
= 索引1中的元素 
= 121的右孩子(索引0)
= 索引(2 * 0 + 2)中的元素
= 索引2中的元素 
= 9相似地,
12的左孩子(索引1)
= 索引(2 * 1 + 1)中的元素
= 索引3中的元素
= 512的右孩子(索引1)
= 索引(2 * 1 + 2)中的元素
= 索引4中的元素
= 6

我们还要确认规则是否适用于寻找任何节点的父节点

9的父母(索引2) 
=(2-1)/ 2 
= 1 / 2
= 0.5
〜索引0 
= 112的父母(索引1) 
=(1-1)/ 2 
= 索引 0
= 1
了解数组索引到树位置的这种映射对于了解堆数据结构如何工作以及如何用于实现堆排序至关重要。

什么是堆数据结构?

堆是一种特殊的基于树的数据结构。如果满足以下条件,则据说二叉树遵循堆数据结构:

  • 它是一个完整的二叉树
  • 树中的所有节点都遵循其大于子节点的属性,即,最大元素位于根及其子节点,并且小于根,依此类推。这样的堆称为最大堆。相反,如果所有节点都小于其子节点,则称为最小堆

以下示例图显示了最大堆和最小堆。

最大堆最小堆比较

最大堆和最小堆

要了解更多信息,请访问堆数据结构。


如何“堆肥”一棵树

从完整的二叉树开始,我们可以通过在堆的所有非叶元素上运行一个称为heapify的函数,将其修改为最大堆。

由于heapify使用递归,因此可能很难掌握。因此,让我们首先考虑如何只用三个元素堆放一棵树。

heapify(array)Root = array[0]Largest = largest( array[0] , array [2*0 + 1]. array[2*0+2])if(Root != Largest)Swap(Root, Largest)
该图显示了heapify的基本情况如何工作

重整基础案例

上面的示例显示了两种情况-一种情况,其中根是最大的元素,而我们无需执行任何操作。另一个根中有一个较大的元素,我们需要交换以维护max-heap属性。

如果您以前使用过递归算法,则可能已经确定这必须是基本情况。

现在让我们考虑另一个场景,其中存在多个级别。

该图显示了具有包含两个最大堆子树的根元素的树数据

当其子树已经是最大堆时,如何堆置根元素

顶部元素不是最大堆,但是所有子树都是最大堆。

为了维持整个树的max-heap属性,我们将不得不继续向下推2直到它到达正确的位置。

当两个子树都已经是max-heaps时,对根元素进行堆放的步骤

当子树为max-heaps时如何堆放根元素

因此,要在两个子树都是max-heap的树中维护max-heap属性,我们需要在根元素上重复运行heapify,直到它大于其子元素或成为叶节点为止。

 

我们可以将这两个条件组合在一个heapify函数中

void heapify(int arr[], int n, int i) {// Find largest among root, left child and right childint largest = i;int left = 2 * i + 1;int right = 2 * i + 2;if (left < n && arr[left] > arr[largest])largest = left;if (right < n && arr[right] > arr[largest])largest = right;// Swap and continue heapifying if root is not largestif (largest != i) {swap(&arr[i], &arr[largest]);heapify(arr, n, largest);}
}

此功能适用于基本情况和任何大小的树。因此,只要子树是最大堆,我们就可以将根元素移动到正确的位置,以保持任何树大小的最大堆状态。


建立最大堆

要从任何树构建最大堆,我们可以从下至上开始对每个子树进行堆放,并在将函数应用于包括根元素的所有元素后以最大堆结束。

对于完整的树,非叶子节点的第一个索引由给出n/2 - 1。之后的所有其他节点都是叶节点,因此不需要进行堆放。

因此,我们可以建立一个最大堆

    // Build heap (rearrange array)for (int i = n / 2 - 1; i >= 0; i--)heapify(arr, n, i);
为堆排序建立最大堆的步骤

创建数组并计算i

为堆排序建立最大堆的步骤

建立用于堆排序的最大堆的步骤

为堆排序建立最大堆的步骤

建立用于堆排序的最大堆的步骤

为堆排序建立最大堆的步骤

建立用于堆排序的最大堆的步骤

如上图所示,我们首先堆放最小的最小树,然后逐渐向上移动直到到达根元素。

恭喜,如果您到此为止都已经了解了所有内容,那么您就可以掌握堆方法了。


堆排序如何工作?

  1. 由于树满足Max-Heap属性,因此最大的项存储在根节点上。
  2. 交换:删除根元素,并将其放在数组的末尾(第n个位置)。将树的最后一项(堆)放在空白处。
  3. 删除:将堆大小减小1。
  4. 堆肥:再次堆肥根元素,以便我们在根上拥有最高的元素。
  5. 重复该过程,直到对列表中的所有项目进行排序为止。
实现堆排序的过程

交换,删除和堆化

下面的代码显示了该操作。

    // Heap sortfor (int i = n - 1; i >= 0; i--) {swap(&arr[0], &arr[i]);// Heapify root element to get highest element at root againheapify(arr, i, 0);}

Java示例

// Heap Sort in Javapublic class HeapSort {public void sort(int arr[]) {int n = arr.length;// Build max heapfor (int i = n / 2 - 1; i >= 0; i--) {heapify(arr, n, i);}// Heap sortfor (int i = n - 1; i >= 0; i--) {int temp = arr[0];arr[0] = arr[i];arr[i] = temp;// Heapify root elementheapify(arr, i, 0);}}void heapify(int arr[], int n, int i) {// Find largest among root, left child and right childint largest = i;int l = 2 * i + 1;int r = 2 * i + 2;if (l < n && arr[l] > arr[largest])largest = l;if (r < n && arr[r] > arr[largest])largest = r;// Swap and continue heapifying if root is not largestif (largest != i) {int swap = arr[i];arr[i] = arr[largest];arr[largest] = swap;heapify(arr, n, largest);}}// Function to print an arraystatic void printArray(int arr[]) {int n = arr.length;for (int i = 0; i < n; ++i)System.out.print(arr[i] + " ");System.out.println();}// Driver codepublic static void main(String args[]) {int arr[] = { 1, 12, 9, 5, 6, 10 };HeapSort hs = new HeapSort();hs.sort(arr);System.out.println("Sorted array is");printArray(arr);}}

堆排序复杂度

O(nlog n)对于所有情况(最佳情况,平均情况和最坏情况),堆排序都有时间复杂性。

让我们了解原因。包含n个元素的完整二叉树的高度为log n

如我们先前所见,要完全堆放其子树已经是max-heaps的元素,我们需要继续比较该元素及其左,右子元素,并将其向下推,直到其两个子元素均小于其大小。

在最坏的情况下,我们需要将元素从根移动到叶节点,进行多次log(n)比较和交换。

在build_max_heap阶段,我们对n/2元素执行此操作,因此build_heap步骤的最坏情况复杂度为n/2*log n ~ nlog n

在排序步骤中,我们将根元素与最后一个元素交换并堆放根元素。对于每个元素,这再次花费log n最糟糕的时间,因为我们可能必须将元素从根到叶一直带到一路。因为我们重复这个ñ次,heap_sort步骤也为nlog n

同样,由于build_max_heapheap_sort步骤是一个接一个地执行的,因此算法的复杂度不会增加,而是保持在的顺序nlog n

它还在O(1)空间复杂度上执行排序。与快速排序相比,它具有更好的最坏情况( O(nlog n) )。快速排序O(n^2)在最坏的情况下具有复杂性。但是在其他情况下,快速排序速度很快。Introsort是堆排序的替代方案,它结合了快速排序和堆排序以保留两者的优点:最坏情况下的堆排序速度和平均速度。


堆分类应用

涉及安全性的系统和嵌入式系统(例如Linux Kernel)之所以使用Heap Sort,是因为O(n log n)Heapsort运行时间的O(1)上限以及其辅助存储的上限是恒定的。

尽管堆排序O(n log n)即使在最坏的情况下也具有时间复杂性,但它没有更多的应用程序(与其他排序算法(如快速排序,合并排序)相比)。但是,如果我们要从项目列表中提取最小(或最大)的数据,而无需保持其余项目按排序顺序的开销,则可以有效地使用其基础数据结构堆。例如,优先队列。


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

相关文章

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

精通八大排序算法系列&#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系统自带的设置应用…

android中PreferencesActivity的使用(一)

在使用android手机的时候&#xff0c;尤其是在操作软件设置时&#xff0c;我们经常见到这样的界面&#xff1a; 这是怎么来实现的的呢&#xff1f;其实android已经提供了相应的类和方法&#xff0c;当进行简单数据存储时&#xff08;比如&#xff1a;软件配置参数&#xff09;a…

PreferenceActivity用法简介(二)

本测试主要是为了测试PreferenceActivity的使用&#xff0c;其中设置了播放背景音乐和开启wifi的设置&#xff0c;也就是本文要讲的 PreferenceActivity。 Android提供了放摆放的工具来定义所有的程序的首选项&#xff0c;并支持既不不许要编写代码的情况写显示这些首选项。可…

Android之PreferenceActivity

看到很多书中都没有对PreferenceActivity做介绍&#xff0c;而我在看Android Samples时无意中看见了&#xff0c;所以就稍微总结一下&#xff0c;也方便日后查找。 PerferenceActivity是什么&#xff0c;看下面的截图&#xff1a; 好了&#xff0c;我们看到Android系统本身就大…

Android PreferenceActivity使用

PreferenceActivity继承了ListActivity&#xff0c;定义Activity继承PreferenceActivity。在res目录下新建一个xml文件夹&#xff0c;接着在这个文件夹下新建一个取名为preferences.xml的File文件&#xff0c;xml中可以使用的标签(Tag)可以分为两类&#xff0c;一类是管理布局的…

PreferenceActivity定制

android很多设置界面都会使用PreferenceActivity来实现&#xff0c;但那个界面比较丑陋&#xff0c;显示开发总是满足不了要求。 可以自己实现一个&#xff0c;但是那样又会使Activity中的逻辑代码和xml布局文件过于复杂&#xff0c;远远不及PreferenceActivity来的方便快捷。…

PreferenceActivity、PreferenceFragment使用

目录 目录前言PreferenceActivity preferences_scenario_1xmlPreference Activity演示 PreferenceFragment xml布局文件Preference FragmentPreference Activity管理Fragment 适配 前言 转来转去又回到了Android&#xff0c;闲话少说&#xff0c;这里是参考Android原生的Setti…

Android PreferenceActivity的介绍

转载&#xff1a;http://blog.chinaunix.net/uid-24666775-id-351136.html 在Android中的APIdemos是中经常遇到过继承于PreferenceActivity这个类&#xff0c;紧接着就是addPreferencesFromResource(R.xml.*******);&#xff08;附&#xff1a;这个******就是一个XML文件&#…