排序算法:堆排序

article/2025/10/29 22:47:30

 

相关博客:

排序算法:冒泡排序、插入排序、选择排序、希尔排序

排序算法:归并排序、快速排序

排序算法:桶排序、计数排序、基数排序

排序算法:堆排序

十大排序算法小结


一、堆:

1、什么是堆:

堆是一种特殊的树,它满足需要满足两个条件:

(1)堆是一种完全二叉树,也就是除了最后一层,其他层的节点个数都是满的,最后一个节点都靠左排列。

(2)堆中每一个节点的值都必须大于等于(或小于等于)其左右子节点的值。

对于每个节点的值都大于等于子树中每个节点值的堆,我们叫作“大顶堆”。对于每个节点的值都小于等于子树中每个节点值的堆,我们叫作“小顶堆”。

2、堆的实现:

用数组来存储完全二叉树是非常节省内存空间的,因为我们不需要存储左右子节点的指针,单纯通过数组的下标,就可以找到一个节点的子节点和父节点。

从图中我们可以看到,数组中下标为 i 的节点的左子节点,就是下标为 i∗2 的节点,右子节点就是下标为 i∗2+1的节点,父节点就是下标为i/2的节点。

3、堆的核心操作:

(1)往堆中插入一个元素:

我们新插入一个元素之后,堆可能就不满足堆的特性了,就需要进行调整,让其重新满足堆的特性,即堆化(heapify),堆化非常简单,就是顺着节点所在的路径,向上或者向下,对比,然后交换。所以分为两种,从下往上 和 从上往下。

①从下往上堆化:

例如:在已经建好的堆中插入值为“22”的结点,这时候就需要重新调整堆结构,过程如下:

Java代码实现:

public class Heap {private int[] a;//数组,从下标1开始存储数据private int n;//堆可以存储的最大数据个数private int count;//堆中已经存储的数据个数public Heap(int capacity){a = new int[capacity+1];n=capacity;count = 0;}//1、插入数据,并从下往上堆化public void insert(int data){if(count>=n) return;//堆满了++count;a[count]=data;int i = count;while(i/2>0 && a[i]>a[i/2]){swap(a,i,i/2);i=i/2;}}private void swap(int[] array, int i, int j) {int temp=array[j];array[j]=array[i];array[i]=temp;}
}

(2)从堆中删除堆顶元素:

删除步骤:把最后一个节点放到堆顶,然后利用同样的父子节点对比方法。对于不满足父子节点大小关系的,互换两个节点,并且重复进行这个过程,直到父子节点之间满足大小关系为止。这就是从上往下的堆化方法

例如:删除堆顶的“33”结点,删除步骤如下:

Java代码实现:

        public void removeMax(){if(count ==0) return;a[1]=a[count];--count;heapity(a,count,1);}//2、自上往下堆化private void heapity(int[] a2, int n, int i) {while(true){int MaxPos=i;if(i*2<=n && a[i]<a[i*2]) MaxPos=i*2;if(i*2+1<=n && a[MaxPos]<a[i*2+1]) MaxPos=i*2+1;if(MaxPos==i) break;swap(a, i, MaxPos);i=MaxPos;}}

一个包含 n个节点的完全二叉树,树的高度不会超过 log2n。堆化的过程是顺着节点所在路径比较交换的,所以堆化的时间复杂度跟树的高度成正比,也就O(logn)。插入数据和删除堆顶元素的主要逻辑就是堆化,所以在堆中插入一个元素和删除堆顶元素的时间复杂度都是O(logn)。


二、堆排序:

1、算法原理:

堆排序是指利用堆这种数据结构进行排序的一种算法。

(1)排序过程中,只需要个别临时存储空间,所以堆排序是原地排序算法,空间复杂度为O(1)。

(2)堆排序的过程分为建堆和排序两大步骤。建堆过程的时间复杂度为O(n),排序过程的时间复杂度为O(nlogn),所以,堆排序整体的时间复杂度为O(nlogn)。

(3)堆排序不是稳定的算法,因为在排序的过程中,存在将堆的最后一个节点跟堆顶节点互换的操作,所以可能改变值相同数据的原始相对顺序。

2、建堆:

(1)第一种是实现思路:借助前面的,在堆中插入元素的思路。将要排序的数组从前往后处理,依次到插入堆中,并且每次都从下往上进行堆化。

(2)第二种实现实现思路:从后往前处理数组,并且每个数据都是从上往下堆化。也就是从二叉树中第一个非叶子节点开始开始堆化,因为叶子节点往下堆化只能自己跟自己比较。如下图:

Java代码实现:

        //3、第二种堆化的方法:public void buildHeap(int[] a,int n){for(int i=n/2;i>=1;--i){heapity(a,n,i);}}//2、自上往下堆化private void heapity(int[] a, int n, int i) {while(true){int MaxPos=i;if(i*2<=n && a[i]<a[i*2]) MaxPos=i*2;if(i*2+1<=n && a[MaxPos]<a[i*2+1]) MaxPos=i*2+1;if(MaxPos==i) break;swap(a, i, MaxPos);i=MaxPos;}}

3、排序:

(1)建堆:建堆结束后,数组中的数据已经是按照大顶堆的特性组织的。数组中的第一个元素就是堆顶。

(2)取出最大值(类似删除操作):将堆顶元素a[1]与最后一个元素a[n]交换,这时,最大元素就放到了下标为n的位置。

(3)重新堆化:交换后新的堆顶可能违反堆的性质,需要重新进行堆化。

(4)重复(2)(3)操作,直到最后堆中只剩下下标为1的元素,排序就完成了。

Java代码实现:

        //4、排序,n表示数据的个数。public void sort(int[] a,int n) {buildHeap(a,n);int k=n;while(k>1){swap(a, 1, k);--k;heapity(a,k,1);}}

 

 

本篇博客主要参考《极客时间》王争的《数据结构与算法之美》专栏第28节。


http://chatgpt.dhexx.cn/article/7IIw8wF0.shtml

相关文章

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

文章目录 堆排序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文件&#…

Android开发--详解SharedPreference/PreferenceActivity

我们经常看到应用程序的设置页面&#xff0c;一般用到设置页面时&#xff0c;我们会继承自PreferenceActivity&#xff0c;它实现了SharedPreference&#xff0c;并生成相应的XML文件自动保存用户的设置&#xff0c;在设置页面中&#xff0c;每一个列表项都是一个Preference&am…

Android之PreferenceActivity 详解

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

android的PreferenceActivity

前言 这段时间在研究android平台上的开源项目——StandupTimer&#xff0c;这是由jwood所设计的一个较为简单android应用&#xff0c;用于控制会议时间&#xff0c;类似秒表倒计时。 PreferenceActivity PreferenceActivity是android提供的对系统信息和配置进行自动保存的Activ…

用StopWatch统计耗时,比System.currentTimeMillis好用

平时项目中统计耗时都用System.currentTimeMillis&#xff0c;最近看到一个spring-StopWatch统计耗时&#xff0c;其用法简单明了&#xff0c;比传统统计耗时方法好用。 StopWatch 的内部是通过 System.nanoTime() 来计时的&#xff0c;本质和 System.currentTimeMillis() 差别…

并发慎用——System.currentTimeMillis()

好记忆不如烂笔头&#xff0c;能记下点东西&#xff0c;就记下点&#xff0c;有时间拿出来看看&#xff0c;也会发觉不一样的感受. System.currentTimeMillis()是极其常用的基础Java API&#xff0c;广泛地用来获取时间戳或测量代码执行时长等&#xff0c;在我们的印象中应该快…

System.currentTimeMillis()的性能问题以及解决方法

System.currentTimeMillis()是极其常用的基础Java API&#xff0c;广泛地用来获取时间戳或测量代码执行时长等&#xff0c;在我们的印象中应该快如闪电。但实际上在并发调用或者特别频繁调用它的情况下&#xff08;比如一个业务繁忙的接口&#xff0c;或者吞吐量大的需要取得时…