点击蓝色字关注我们!

什么是堆

「堆」首先是一个完全二叉树,「堆」分为「大顶堆」「小顶堆」
「大顶堆」 :
每个节点的值大于或等于其左右孩子节点的值,称为大顶堆。
「小顶堆」同理就是每个节点的值小于或等于其左右孩子节点的值。
「注意」:
每个节点的左右孩子节点的大小关系并没有限定。

大顶堆举例

如图:

大顶堆举例

首先其为一个完全二叉树,且其每个节点的值都大于或者等于其左右孩子节点的值。
完全二叉树从上到下,从左到右依次编号,就可以将其进行顺序存储,我们从根节点开始,从0开始编号,存入数组如下:

大顶堆存入数组举例

堆特点

由大顶堆定义知道,如果我们从上到下,从左到右,根节点开始从0编号进行顺序存储的话,并将数组记为arr;
我们可以得到如下式子:
arr[i] >= arr[ 2i + 1]  && arr[ i ] >= arr[ 2i + 2];
其中 2i + 1为第 i 个节点的左孩子节点的编号。2i + 2为第 i 个节点的右孩子节点的编号;
同理得小顶堆的特点:
arr[i] <= arr[ 2i + 1]  && arr[ i ] <= arr[ 2i + 2];

堆排序基本思想

本文以大顶堆为例,进行讲解。
算法步骤如下:
1、首先将待排序序列构建成一个大顶堆(存入数组中),那么这时,整个序列的最大值就是堆顶的根节点;
2、将堆顶元素与最后一个元素交换,那么末尾元素就存入了最大值;
3、将剩余的 n - 1个元素重新构建成一个大顶堆,重复上面的操作;
反复执行,就能得到一个有序序列了。

举例

给定一个待排序序列数组 arr = [ 0 , 2,  4,  1 , 5 ];
先构建成一个完全二叉树如下;

初始状态

构建堆

「我们从最后一个非叶子节点开始,从左至右,从下到上,开始调整」
最后一个非叶子节点的索引即 arr.length / 2向下取整 - 1 ,对于此例就是 5 / 2向下取整 - 1 = 2 - 1 = 1;
即值为2的节点;

构建堆1

我们用左右孩子节点的最大值与该节点进行比较;
此时我们发现它的左右孩子节点的最大值为5,大于2,进行交换;

构建堆2

然后处理下一个非叶子节点,即刚才的索引减去1;1 - 1 = 0;
即:

构建堆3

左右孩子节点为5和4,5最大,且大于该节点的值,发生交换;

构建堆4

这时我们发现了一个问题:
「值为0的节点的左右节点又比该节点大了,又不满足大顶堆的定义了」

继续进行调整:

构建堆5

对非叶子节点调整完毕,构建大顶堆完成。

交换

将堆顶元素与末尾元素进行交换,使得末尾元素最大。

堆顶元素与末尾元素交换

当交换完毕后最大的元素已经到达数组末尾;

第一次交换后

对数组中其他元素进行排序即可。

剩下的四个元素进行调整

进行交换:

第二大元素归位

剩下的元素调整并交换后:

第三大元素归位

剩下的元素调整并交换后:

第三大元素归位

第四大元素归位置

此时也意味着排序完成了。

代码

先说下调整的代码;
我们需要三个参数,待排序的数组,数组的长度,还有一个就是调整的哪一个非叶子节点;

 /*** author:微信公众号:code随笔* @param arr 待排序的数组* @param i   表示等待调整的哪个非叶子节点的索引* @param length 待调整长度*/public static void adjustHeap(int arr[],int i,int length){//非叶子节点的值int notLeafNodeVal = arr[i];//k的初始值为当前非叶子节点的左孩子节点的索引//k = 2 * k + 1表示再往左子节点找for(int k = i * 2 + 1;k<length;k=2 *k + 1){//如果k + 1还在待调整的长度内,且右子树的值大于等于左子树的值//将k++,此时为当前节点的右孩子节点的索引if(k+1<length && arr[k] < arr[k+1]){k++;}//如果孩子节点大于当前非叶子节点if(arr[k] > notLeafNodeVal){arr[i] = arr[k];//将当前节点赋值为孩子节点的值i = k;//将i赋值为孩子节点的值,再看其孩子节点是否有比它大的}else{break;//能够break的保证是,我们是从左至右,从下到上进行调整的//只要上面的不大于,下面的必不大于}}//循环结束后,将i索引处的节点赋值为之前存的那个非叶子节点的值arr[i] = notLeafNodeVal;}

再说下堆排序代码,看好注释;

//堆排序方法public static void heapSort(int arr[]){//进行第一次调整for(int i=arr.length/2 - 1;i>=0;i--){adjustHeap(arr,i,arr.length);}for(int j=arr.length - 1;j>0;j--){//进行交换int temp = arr[j];arr[j] = arr[0];arr[0] = temp;//调整长度为j的那些//这里为什么填0呢//因为我们第一次调整的时候从左到右,从下到上调整的;//交换时只是变动了堆顶元素和末尾元素//末尾元素我们不用去管,因为已经是之前长度最大的了//只需要把当前堆顶元素找到合适的位置即可adjustHeap(arr,0,j);}}

完整代码

import java.util.Arrays;public class Solution {public static void main(String[] args) {int [] arr = new int[]{0 , 2,  4,  1 , 5};heapSort(arr);System.out.println(Arrays.toString(arr));}//堆排序方法public static void heapSort(int arr[]){//进行第一次调整for(int i=arr.length/2 - 1;i>=0;i--){adjustHeap(arr,i,arr.length);}for(int j=arr.length - 1;j>0;j--){//进行交换int temp = arr[j];arr[j] = arr[0];arr[0] = temp;//调整长度为j的那些//这里为什么填0呢//因为我们第一次调整的时候从左到右,从下到上调整的;//交换时只是变动了堆顶元素和末尾元素//末尾元素我们不用去管,因为已经是之前长度最大的了//只需要把当前堆顶元素找到合适的位置即可adjustHeap(arr,0,j);}}/*** author:微信公众号:code随笔* @param arr 待排序的数组* @param i   表示等待调整的哪个非叶子节点的索引* @param length 待调整长度*/public static void adjustHeap(int arr[],int i,int length){//非叶子节点的值int notLeafNodeVal = arr[i];//k的初始值为当前非叶子节点的左孩子节点的索引//k = 2 * k + 1表示再往左子节点找for(int k = i * 2 + 1;k<length;k=2 *k + 1){//如果k + 1还在待调整的长度内,且右子树的值大于等于左子树的值//将k++,此时为当前节点的右孩子节点的索引if(k+1<length && arr[k] < arr[k+1]){k++;}//如果孩子节点大于当前非叶子节点if(arr[k] > notLeafNodeVal){arr[i] = arr[k];//将当前节点赋值为孩子节点的值i = k;//将i赋值为孩子节点的值,再看其孩子节点是否有比它大的}else{break;//能够break的保证是,我们是从左至右,从下到上进行调整的//只要上面的不大于,下面的必不大于}}//循环结束后,将i索引处的节点赋值为之前存的那个非叶子节点的值arr[i] = notLeafNodeVal;}
}

时间复杂度

在建初始堆时,其复杂度为;
交换操作需 n-1 次;
重建堆的过程中近似为;
堆排序时间复杂度为。

稳定性

堆排序是不稳定的:
比如:10,9,6,9;如图:

稳定性分析用图

当堆顶元素10和末尾元素交换后,两个9的相对位置发生改变。

已发布:

【算法知识】详解冒泡算法

【算法知识】详解选择排序算法

【算法知识】详解插入排序算法

【算法知识】详解快速排序算法

【算法知识】详解基数排序算法

往期精彩回顾适合初学者入门人工智能的路线及资料下载机器学习及深度学习笔记等资料打印机器学习在线手册深度学习笔记专辑AI基础下载(pdf更新到25集)机器学习的数学基础专辑本站qq群1003271085,加入微信群请回复“加群”获取一折本站知识星球优惠券,复制链接直接打开:https://t.zsxq.com/yFQV7am喜欢文章,点个在看

【算法知识】详解堆排序算法相关推荐

  1. 算法-详解堆排序算法

    title: 算法-详解堆排序算法 date: 2017-07-06 22:00:16 categories: 算法,面试 tags: [算法,Algorithm,面试,排序] description ...

  2. 堆排序 java_详解堆排序算法原理及Java版的代码实现

    概述堆排序是一种树形选择排序,是对直接选择排序的有效改进. 堆的定义如下:具有n个元素的序列(k1,k2,...,kn), 当且仅当满足: 时称之为堆.由堆的定义可以看出,堆顶元素(即第一个元素)必为 ...

  3. xgboost算法_详解xgboost算法的样本不平衡问题

    XGBoost官方文档对参数scale_pos_weight的定义: 翻译: 调节正负样本权重的平衡 ,常用来处理不平衡的正负样本数据 . 典型值算法: scale_pos_weight = 负样本总 ...

  4. 什么是DES算法,详解DES算法的基本原理

    DES算法是应用最为广泛的对称加密算法.它主要应用在计算机网络通信.电子资金传送系统.保护用户文件,此外,DES还可用于计算机用户识别系统中.那么,具体什么是DES算法,DES算法的基本原理是什么,本 ...

  5. 游戏洗牌算法——常用+详解最优Knuth_Durstenfeld算法

    目录 前言 基于Unity的洗牌算法代码实现 内容 抽牌洗牌 原理 复杂度 优缺点 Fisher_Yates算法 原理 复杂度 代码实现 优缺点 Knuth_Durstenfeld算法(最佳洗牌算法) ...

  6. 【算法知识】详解基数排序算法

    已发布: [算法知识]详解选择冒泡算法 [算法知识]详解选择排序算法 [算法知识]详解插入排序算法 [算法知识]详解快速排序算法 [算法知识]详解归并排序算法 基本思想 基数排序的思想是将整数按位数切 ...

  7. 【算法知识】详解归并排序算法

    已发布: [算法知识]详解选择冒泡算法 [算法知识]详解选择排序算法 [算法知识]详解插入排序算法 [算法知识]详解快速排序算法 基本思想 归并排序的基本思想是: 先将序列一次次分成子序列,直到子序列 ...

  8. 【算法知识】详解快速排序算法

    基本思想 已发布: [算法知识]详解选择冒泡算法 [算法知识]详解选择排序算法 [算法知识]详解插入排序算法 本文的思路是以从小到大为例讲的. 快速排序的基本思想是任取待排序序列的一个元素作为中心元素 ...

  9. js排序算法详解-堆排序

    全栈工程师开发手册 (作者:栾鹏) js系列教程5-数据结构和算法全解 js排序算法详解-堆排序 这种排序方式呢,理论性太强,看动图的时候满脸写着懵逼,多看几遍似乎明白了编者的意图,但是要把这种理论的 ...

最新文章

  1. 什么?还有可以攻击telegram和其他APP的恶意软件
  2. promehteus 监控超时_05 . Prometheus监控Nginx
  3. .NET中的高性能应用
  4. 一个问题就可以辨别真假NLP(自然语言处理)研究者
  5. 下面选项中不是开发java程序的步骤是_Java基础试题及其答案2
  6. 在SharePoint网站中访问Webservice被拒绝的解决方法
  7. java xframeoptions,Header:X-Frame-Options开启与关闭方法
  8. c语言中字符怎么表示6,6、C语言中的字符串
  9. [BZOJ2440][中山市选2011]完全平方数(莫比乌斯函数,二分)
  10. FocusPoint.js实现图片响应
  11. mkfs 格式化分区并创建文件系统
  12. cydia无法加载未能连接服务器,cydia无法加载_Cydia无法加载如何办?Cydia加载失败故障的解决方...
  13. 这里有110+公开的专业数据集
  14. 地信实验一利用矢量化软件AutoCAD对栅格文件矢量化
  15. SpringBoot使用easyexcel打印数据
  16. coreldraw x4怎么写字_CorelDRAW实例:给文字排版的几个小技巧(4)
  17. 什么是进程? 为什么使用进程? 如何运用进程?
  18. MySQL---DDL语句、DML语句与DCL语句
  19. 圆周率一千万亿位_目前圆周率已经达到10万亿位了,为何还在不停地计算圆周率?...
  20. 三步搭建免费无限空间无限速网盘——基于docker与阿里云镜像(B站学习)

热门文章

  1. PHP学习之Smarty+CSS+DIV页面居中问题
  2. python8.3多进程
  3. kafka详解 转载
  4. Js获取地址栏参数值
  5. 一文攻破共用体-C语言
  6. 第二天学习Java的笔记
  7. 转载:【opencv入门教程之一】:配置
  8. PowerDesigner基本使用
  9. JDBC(一)——statement对象、PreparedStatement对象
  10. 页面怎么把关键字保留下来_怎么做seo优化,以及网站SEO优化计划!