9.7 堆

9.7.1 堆的定义与基本操作

堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子结点的值。其中,如果父亲结点的值大于或等于孩子结点的值,那么称这样的堆为大顶堆,这时,每个结点的值都是以它为根结点的子树的最大值。相反,则为小顶堆

那么对于一个给定的初始序列,怎样把它建成一个堆呢?

从最后一个元素开始,从下往上,从右往左。假设当前元素X,让x与X的孩子结点比较,如果发现比X更大的元素Y,则交换X与Y的位置,这样Y就成了根结点,而X则成为了孩子结点。交换之后让X继续与其孩子结点比较,直到X的孩子结点都比X小或没有孩子节点为止。

例如:

从下往上,从右往左

  • 第一个有孩子结点的是巨门,但是天同比巨门小,不同调整。
  • 天机:七杀比太阴大,天机与七杀相比七杀大,交换七杀与天机的位置。交换后天机没有孩子结点,调整结束。

  • 贪狼:紫薇比破军大,且比贪狼大,交换贪狼和紫薇。交换后贪狼没有孩子结点,调整结束。

  • 武曲:七杀比巨门大,也比武曲大,交换武曲与七杀,交换后武曲有孩子天机和太阴,继续调整。太阴比天机大,也比武曲大,交换武曲与太阴的位置,交换后的武曲没有孩子节点,调整结束。

  • 廉贞:紫薇比七杀大,比廉贞大,交换廉贞与紫薇的位置,交换后廉贞有孩子破军和贪狼,继续调整。破军比贪狼,廉贞大,交换廉贞与破军的位置,交换后廉贞没有孩子结点,调整结束。

至此,建堆完成。

那具体是怎么实现的呢,对完全二叉树来说,使用数组存储完全二叉树。这样结点就按层序存储在数组中,其中第一个节点将存储于数组中的1号位,并且数组i号位表示的结点的左孩子是(2i)号位,有孩子则是(2i+1)号位。则可以这样定义数组来表示堆

const int maxn = 100 ;
//head为堆,n为元素个数
int heap[maxn] , n = 10 ;

根据上面建堆的过程,每次调整都是把结点从上往下调整。针对这种向下调整,调整的方法是这样的:总是将当前结点V与它的左右孩子比较(如果有的话),假如孩子中存在比当前结点V权值大的,就将其中权值最大的孩子结点与结点V交换;交换完毕后继续让结点V和孩子比较,直到结点V的孩子的权值都比结点V的权值小或结点V没有孩子结点。

代码如下,时间复杂度为O(logN):

//对heap数组在[low,high]范围内进行调整
//low为欲调整结点的数组下标,high一般为堆的最后一个元素数组下标
void downAdjust( int low , int high ){int i = low , j = 2 * i ;  //i为欲调整结点,j为其左孩子结点while( j <= high ){ //存在孩子结点//右孩子存在并且比左孩子大if( j+1 <= high && heap[j+1] > heap[j] ){j = j+1 ;  //j记录右孩子的数组下标}//孩子节点比当前节点大if( heap[j] > heap[i] ){swap( heap[j] , heap[i] ) ;  //交换两个结点i = j ;     //保持i为欲调整结点、j为i的左孩子j = 2 * i ;}else{break ;  //孩子结点的权值均比欲调整结点i小,调整结束}}}

那么建堆的过程就很容易了。假设序列中元素个数为n,由于完全二叉树的叶子节点个数为向上取整(n/2),因此数组下标在 [ 1 , 向下取整(n/2) ] 范围内的结点都是非叶子结点。于是可以从 向下取整(n/2) 号位开始 倒着 枚举结点,对每个遍历到的结点i进行[i,n]范围内的调整。
为什么要倒着枚举呢?
这是因为每次调整完一个结点后,当前子树中权值最大的结点就会处在根结点的位置,这样当遍历到其父亲节点时,就可以直接使用这个结果。符合从下往上,从右往左的规则

建堆代码如下,时间复杂度位O(N)

//建堆
void createHeap(){for( int i = n / 2 ; i >= 1 ; i-- ){downAdjust(i , n) ;}
}

另外,如果要删除堆中的最大元素(也就是堆顶元素),并让其仍然保持堆的结构,那么只需要将最后一个元素覆盖堆顶元素,然后对根结点进行调整,时间复杂度为O(logN)

//删除堆顶元素
void deleteTop(){heap[1] = heap[n--] ;  //用最后一个元素覆盖堆顶元素,并让元素个数减一downAdjust(1 , n) ;  //向下调整元素}

如果想要往堆里添加一个元素,可以把想要添加的元素放在数组最后(也就是完全二叉树的最后一个节点后面),然后进行向上调整。 向上调整总是把欲调整结点与父亲结点比较,如果权值比父亲节点大,那么就交换其与父亲结点,这样反复比较,直到到达顶堆或父亲结点权值较大为止。

//对heap数组在[low,high]范围进行向上调整
//其中low一般设置为1,high表示欲调整结点的数组下标
void upAdjust( int low , int high ){int i = high , j = i / 2 ;   //i为欲调整结点,j为其父亲结点while( j >= low ){  //存在父亲结点if( heap[j] < heap[i] ){    //父亲结点权值小于欲调整结点swap( heap[j] , heap[i] ) ; //交换i = j ;j = i / 2 ;}else{break ;}}
}

在此基础之上,很容易实现添加元素的代码

//添加元素x
void insert( int x ){heap[++n] = x ;   //让元素个数加1,然后将数组末位赋值为xupAdjust(1 , n) ;  //向上调整加入的结点n
}

9.7.2 堆排序

堆排序是指使用堆结构对一个序列进行排序。现在讨论递增排序的情况。
考虑对一个堆来说,堆顶元素是最大的,因此在建堆完毕后,堆排序的直观思路就是取出堆顶元素,然后将堆的最后一个元素替换至堆顶,再进行一次针对堆顶元素的向下调整——如此重复,直到堆中只剩下一个元素为止。

//堆排序
void heapSort(){createHeap() ; //建堆for( int i = n ; i > 1 ; i-- ){ //倒着枚举,直到堆中只有一个元素swap( heap[i] , heap[1] ) ; //交换heap[i]与堆顶downAdjust(1 , i-1) ;  //调整堆顶

《算法笔记》9.7 堆相关推荐

  1. 漫画算法笔记 二叉堆基本操作

    漫画算法笔记 二叉堆基本操作 #include <iostream> #include <stdlib.h> #include <vector> using nam ...

  2. 《算法笔记》中文版 - 包括数组,链表,树,图,递归,DP,有序表等相关数据结构与算法的讲解及代码实现...

    来源:专知本文为资源,建议阅读5分钟本文为你分享<算法笔记>中文版. https://github.com/Dairongpeng/algorithm-note 目录概览 第一节 复杂度. ...

  3. java 寻找和为定值的多个数_算法笔记_037:寻找和为定值的两个数(Java)

    1 问题描述 输入一个整数数组和一个整数,在数组中查找两个数,满足他们的和正好是输入的那个整数.如果有多对数的和等于输入的整数,输出任意一对即可.例如,如果输入数组[1,2,4,5,7,11,15]和 ...

  4. 算法笔记(胡凡)学习笔记@Kaysen

    本文旨在记录算法笔记学习过程中的收获和一些知识点,部分易错知识点只针对个人而言,CCF-CSP考试冲鸭!!! Chapter 2 C/C++快速入门(易错知识点) 2.1 基本数据类型 变量定义注意区 ...

  5. 数据结构与算法笔记(青岛大学王卓老师视频)

    写在前面的话: 因为在学习数据结构之前,学习过一年的算法,所以有一些基础,一些我觉得 没必要的代码或知识就没写上,记得多是一些知识点,写的可能对于别人来说 很难接受,望谅解.我学习算法是在Acwing ...

  6. codeup墓地目录(算法笔记习题刷题笔记)

    在线codeup contest 地址:http://codeup.cn/contest.php Contest100000575 - <算法笔记>3.1小节--入门模拟->简单模拟 ...

  7. 【算法笔记】极客时间 算法面试通关40讲 笔记  覃超

    [算法笔记]极客时间 算法面试通关40讲 覃超 [算法笔记]极客时间 算法面试通关40讲 覃超 相关链接 在leetcode 上的题号 数组.链表: (堆)栈stack.队列queue 优先队列 哈希 ...

  8. 算法笔记知识点整理大全

    每次刷题都觉得自己吃了知识点不全,基础不牢固的亏,刷题的时候目标也不明确,于是看完了算法笔记并把知识点归纳了一下,当然直接看书会更加详细,这个归纳只是学习时加深印象以及方便自己之后回顾而已:之后刷题大 ...

  9. 算法笔记学习PAT甲级解题记录

    算法笔记学习记录 2019.06.26 float&&double 推荐全部使用double,注意区分scanf("%lf",&double1);与prin ...

  10. 考研算法笔记(排序)

    考纲 (只考虑内部排序) 1插入排序(直插(稳),希尔) 2交换排序(冒泡(稳),快排) 3选择排序(简选,堆排) 4归并排序(稳) 5基数排序(稳) 6算法笔记 对任意n个关键字排序的比较次数至少为 ...

最新文章

  1. yocto linux dns,yocto-sumo源码解析(一): o
  2. tensorflow从入门到放弃-0
  3. 利用opencv读取图片将其作为opengl的纹理图片的实现方法
  4. 洛谷 - P2444 - 病毒 - AC自动机
  5. QML资源加载和网络透明度
  6. 学习 shell —— 编写基本脚本
  7. 一步步编写操作系统 1 部署工作环境 1
  8. CVS配置过程 (部分转)
  9. linux如何手动释放内存吗,Linux如何手动清理内存中cache信息
  10. 基于Python+tkinter+pygame的音乐播放器完整源码
  11. 小技巧:Mac下快速锁屏
  12. 常用圆圈数字序号(1~50)
  13. 服装进销存管理软件榜单前十排名
  14. 差距越来越大, 直播行业割终结束, 虎牙、斗鱼平分天下?
  15. helm 简介与入门
  16. word中安装Zotero插件
  17. 支持H.265视频网页Web播放的EasyPlayer.js设计理念与功能计划
  18. 2022年最新前端零基础学习路径
  19. linux内存占用过高怎么解决,centos7内存占用过高处理方法
  20. 快手:看见每一种生活(一面凉经)

热门文章

  1. 无线串口服务器规模,无线串口服务器
  2. 清华社英语在线自动教程python版
  3. ffmpeg解复用FLV文件
  4. atomic头文件编译_atomic
  5. Excel VBA操作网页 显示滚动进度条
  6. vue3 vite版本 引入本地静态图片的方式
  7. JS移动DOM节点,将某节点下所有子节点移动(剪切)到另一个节点下。新手很容易踩的坑!
  8. JavaMail学习6 发送邮件
  9. 【图像压缩】基于matlab余弦变换及霍夫曼编码jpeg压缩和解压【含Matlab源码 2086期】
  10. 《人机交互技术》第二章 感知和认知基础