堆排序算法实现

article/2025/10/29 15:24:20

堆排序:结构逻辑上是完全二叉树,但是可以使用顺序存储来实现

一些二叉树的区别:

二叉树:度数最大为2并且每个子树也是二叉树

二叉树:每层节点都是满的,没有空缺,也就是,叶子节点只能出现在最后一层
在这里插入图片描述

完全二叉树:限制条件比满二叉树弱化,只需要前k-1层是满二叉树结构,最后一层的叶子节点都靠左排列,右侧可以出出现连续缺失(一个学序列可以按照编号一一对应上满二叉树的节点编号)

在这里插入图片描述

以下不是满二叉树结构:

在这里插入图片描述在这里插入图片描述
排序二叉树: 二叉树的基础上,加上 中序遍历是有序输出这一要求
平衡二叉树:排序二叉树的基础上,加上要求左右子树的深度差的绝对值只能是0或者1

现在回到堆排序:
具体满足什么条件的才是堆呢?
以大根堆为例: 对于每棵子树,都满足 根与左右孩子节点相比中最大的元素

堆排序的算法流程:

  1. 堆的初始化(将输入序列调整为符合堆的性质)
  2. 输出堆顶元素(将堆顶元素与堆尾元素交换,堆的大小-1)
  3. 调整堆结构
  4. 重复2 3 ,直到全部输出(堆大小变为0)

假设堆的编号是从0 开始
那么编号i的节点 (这规律没啥记忆的,画几个圈看下规律即可)
父节点:(i-1)/2
左孩子: 2*i+1
右孩子:2*i+2
最后一个非叶子节点编号: 假设堆共有n个节点,那么最后一个非叶子节点编号为 n/2-1

java代码实现:


/*** @author  wangwei* @date     2019/3/9 8:52* @classDescription   完全二叉树:比满二叉树条件稍弱*                      一共k层的完全二叉树,只要求第k-1层是满的*                      并且第k层的节点集中在左侧,也就是右侧可能出现连续缺失情况**                      大顶堆:根最大,每课子树只保证根大于左孩子,大于右孩子,但是不保证左右孩子之间的大小关系**          给定序列,如何判断是否为堆呢?*          将序列按照层次遍历的形式,构建完全二叉树,观察即可*          比如:1  4 3  7 8 5*              1*             / \*            4   3*          /  \  / \*          7   8 5*    显然是小根堆**    堆排序思想:*    1.以初始关键字序列,构建堆*    2.输出堆顶最小元素*    3,调整剩余元素,使其成为新堆*    4,重复2,3 直到n个元素全部输出,得到一个有序序列**    如果输入为数组:*    当前为i  (i从1开始)*    则左孩子为 2*i*     右孩子:   2*i+1***     堆调整:将堆顶输出,最后孩子放置在堆顶,对剩余元素进行调整*            新堆顶与左右孩子比较,*           与较小孩子交换*           直到调整到叶子节点或者是 比左右孩子都小时,停止调整*      如何初始化序列建堆:从最后一个非叶子节点开始,向上调整,直到根节点*      //      最后一个非叶子节点是  n/2  向下取整****      问题:为什么堆适合线性存储*      答:因为堆是完全二叉树结构,堆中元素的位置能根据父节点索引计算得到,*      所以不需要左右指针也可以找到子节点的位置**/
public class HeapSort {public  void init(int []array){if(null==array||0==array.length){return;}// 注意:计算  左孩子 2*i  ,这里表示编号是从1 开始//               数组从0开始,则需要是 2*i+1//               右孩子:2*i+2//  最后一个非叶子节点 索引:  n/2-1// 最后一个非叶子节点,可能只有左孩子for(int index=array.length/2-1;index>=0;index--){int parent=array[index];int lChild=array[2*index+1];// 只有左孩子if(2*index+2>array.length-1){if(parent>lChild){array[index]=lChild;array[2*index+1]=parent;}}// 左右孩子都有else {int rChild=array[2*index+2];// 处理  parent 不是三者之中最小的int minChildIndex=lChild<rChild? 2*index+1:2*index+2;if(parent>array[minChildIndex]){array[index]=array[minChildIndex];array[minChildIndex]=parent;}}}}//      waitAdjust  堆顶元素,此函数就是为了将其调整到特定位置,满足小顶堆//    索引在 minHeapEnd  是表示当前堆的最后一个元素索引//public void adjustHeap(int []array,int minHeapEnd){int currentIndex=0;int waitAdjust=array[0];while (currentIndex<=minHeapEnd/2-1){int leftChild=array[2*currentIndex+1];// 只有左孩子if(2*currentIndex+2>minHeapEnd){if(leftChild>waitAdjust){array[currentIndex]=leftChild;currentIndex=2*currentIndex+1;// 走向左子树}}// 左右孩子都有: 选取最小孩子交换else{int rChild=array[2*currentIndex+2];int minChildIndex=leftChild<rChild? 2*currentIndex+1:2*currentIndex+2;if(waitAdjust<=array[minChildIndex]){break; // 结束调整}else {array[currentIndex]=array[minChildIndex];currentIndex=minChildIndex;}}}array[currentIndex]=waitAdjust;}//将堆顶元素交换到tail  (这里看作是输出)public void pop(int[] array,int tail){int temp=array[0];array[0]=array[tail];array[tail]=temp;}public void heapSort(int array[]){if(null==array||0==array.length){return;}init(array);for(int i=0;i<array.length;i++){pop(array,array.length-1-i);// 注意堆调整的范围比之前的少一个元素adjustHeap(array,array.length-1-i-1);}}public static void main(String[] args) {int [] array= {2,5,3,12,8,17,10,20,19,13};RandomUtil.printArray(array);new HeapSort().heapSort(array);System.out.println("排序后:");RandomUtil.printArray(array);}
}

输出:
在这里插入图片描述

几个方法说明:

init(): 将输入序列初始化为堆
pop(int[] array,int tail): 输出,将堆顶元素交换到当前堆的 ** 最后一个位置 **
adjustHeap(int []array,int minHeapEnd): 调整堆,堆顶元素是来自堆尾的,可能不满足堆的性质,需要将其调整到
合适的位置
时间复杂度:初始化是o(n) ,调整重建是o(lgn),共需要n次重建 记为o(nlgn)
流程是:
初始化;// o(n)
while(i++<array.length){//o(n)
pop();
adjust();//(o lgn)

总的时间复杂度=o(n)+o(nlgn)=o(nlgn)
空间复杂度:只是使用了有限个简单变量,o(1)

注意:堆初始化和堆调整有些相似,但是是不一样的.
    堆初始化:自底向上调整
    堆调整:自上向下重建堆

堆排序的优化(只是一个思路,未证明):

(小顶堆为例)
堆排序哪里可以优化呢,都这么优秀了?
堆调整可能被优化,将堆尾元素与堆顶交换,而堆尾元素可能是是很大的,那么调整时,就会与很多层去比较,如何减少这个层的比较呢?

堆中从根节点到叶节点的一条路径是有序的

    3/  \ 4   5/ \  /\7  6  8 10
现在需要将堆顶给移出,交换成如下树  h=310/  \ 4   5/ \  /\7  6  8 3
// h/2=1  
// 根 10  与4 比较 10>4
// 交换 根成为44/  \ 10  5/ \  /\7  6  8 3//   1. 调整子树   10/  \7   6// 2. 根4与5比较,,根较小,不调整

可能的运用场景

top(k):时间复杂度  n(lgk)


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

相关文章

数据结构之堆排序算法详解+C语言实现

堆   堆是具有以下性质的完全二叉树&#xff1a;每个结点的值都大于或等于其左右孩子结点的值&#xff0c;称为大顶堆&#xff1b;或者每个结点的值都小于或等于其左右孩子结点的值&#xff0c;称为小顶堆。 堆排序   堆排序是利用堆这种数据结构而设计的一种排序算法&…

堆排序算法原理及实现

堆排序是排序中一种比较重要的算法&#xff0c;和快速排序一样&#xff0c;其复杂度也是O(nlogn)&#xff1b;同时也是一种原地排序算法&#xff1a;在任何时候&#xff0c;数组中只有常数个元素存储在输入数组以外。堆这种数据结构是处理海量数据比较常见的结构&#xff0c;海…

堆排序算法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;而堆排序就是基于这种结构而产生的一种程序算法。 堆的分类 大根堆:每个节点的值都大于或者等于他的左右孩子节点的值 小根堆:每个结点的值都小于或等于其左孩子和右孩子…