堆和堆排序

在讨论「堆排序」之前,先复习一下「选择排序」。

void SelectionSort(int a[], size_t n) {for (size_t i = 0; i < n; ++i) {// 在剩余元素中找出最小的一个,然后与 a[i] 交换。size_t k = i;for (size_t j = i+1; j < n; ++j) {if (a[j] < a[k]) {k = j;}}if (k != i) {std::swap(a[i], a[k]);}}
}

选择排序的空间效率很高(O(1)),但是时间效率很低(O(N^2)),主要花在了「从剩余元素中找出最小元素」,每次都要遍历剩余的全部元素。

有没有一种数据结构,能够方便的拿到 最小 元素?

如果重写 SelectionSort,改为反向遍历,每次「从剩余元素中找出最大元素」:

void SelectionSort(int a[], size_t n) {for (size_t i = n-1; i > 0; --i) {// 在剩余元素中找出最大的一个,然后与 a[i] 交换。size_t k = i;for (size_t j = 0; j < i; ++j) {if (a[j] > a[k]) {k = j;}}if (k != i) {std::swap(a[i], a[k]);}}
}

那么问题就可以改成:有没有一种数据结构,能够方便的拿到 最大 元素?

堆,就是这样一种数据结构。

其实堆有「最大堆」和「最小堆」之分,但是差别不大,这里以最大堆为例,是为了便于实现堆排序,这也是改写 SelectionSort 的原因。

堆是一种在数组上实现的几乎完全的二叉树,子节点小于父节点,所以根节点总是最大。

H[1..n] 为堆,以 H[i] 表示数组中第 i 个元素,它的父节点位于 i/2,子节点分别位于 i*2i*2+1

这种通过数组下标的关系来连接父子节点的方式,比一般的树型节点省了两个指针的空间。

给定一个堆 [ 30, 26, 13, 17, 11, 7, 8, 10, 4, 3 ],那么对应的树型结构为:

                30/      \26          13/     \      /   \17      11   7     8/   \    /10     4  3

假如有一个数组 [ 4, 3, 7, 10, 11, 13, 8, 26, 17, 30 ],怎么把它转换成堆呢?

              4/      \3          7/     \    /    \10      11  13     8/   \    /26   17  30

从最小最靠近叶节点的子树开始,如果根节点比子节点小,就与之交换,依次按如下步骤进行调整。

第一步:

   11*             30/       -->     /30             11*

第二步:

    10*            26/   \   -->    /   \26   17        10*   17

第三步:

    7*             13/   \   -->    /   \13    8        7*    8

第四步:

         3*/     \26      30/   \    /10   17  11-->30/     \26      3*/   \    /10    17  11-->30/     \26      11/   \    /10    17  3*

第五步:

              4*/      \30        13/     \     /  \26      11  7    8/   \    /10    17  3-->30/      \4*        13/     \     /  \26      11  7    8/   \    /10    17  3-->30/      \26        13/     \     /  \4*      11  7    8/   \    /10    17  3-->30/      \26        13/     \     /  \17      11  7    8/   \    /10    4*  3

经过这五步,堆就建好了。下面以 C++ 代码示范。

MakeHeap 把一个数组转换成堆:

void MakeHeap(A& a) {for (size_t i = a.size() / 2; i > 0; --i) {SiftDown(a, a.size(), i);}
}

类型 A 就是 std::vector。当然用 C 数组也可以,只是后续讨论插入操作时会比较麻烦。

typedef std::vector<int> A;

MakeHeap 通过 SiftDown 把每棵子树的根节点向下调整。

// SiftDown 把堆 h[1..n] 的第 i 个元素向下调整(i 从 1 打头)。
void SiftDown(A& h, size_t n, size_t i) {while (true) {i = i * 2;if (i > n) {break;}if (i < n && h[i] > h[i-1]) {++i;}if (h[i-1] > h[i/2-1]) {std::swap(h[i-1], h[i/2-1]);} else {break;}}
}

MakeHeap 的用法:

int data[10] = { 4, 30, 8, 17, 26, 13, 7, 3, 10, 11 };
A a(data, data + 10);
MakeHeap(a);

堆排序:

void HeapSort(A& a) {MakeHeap(a);  // 首先把数组 a 转换成堆。// 反向遍历,每次把堆的根与第 i 个元素交换。// 每次交换后,用 SiftDown 把新的根向下调整。for (size_t i = a.size(); i > 1; --i) {std::swap(a[0], a[i-1]);SiftDown(a, i-1, 1);}
}

堆排序与选择排序极为相似,空间效率一样,时间效率更优(O(N*logN))。

SiftDown 相反的操作为 SiftUp

// SiftUp 把堆 h[1..n] 的第 i 个元素向上调整(i 从 1 打头)。
void SiftUp(A& h, size_t i) {while (i > 1) {if (h[i-1] > h[i/2-1]) {std::swap(h[i-1], h[i/2-1]);}i = i / 2;}
}

插入操作依赖于 SiftUp:先添加到数组末尾,然后通过 SiftUp 把新元素向上调整。

void Insert(A& h, int x) {h.push_back(x);SiftUp(h, h.size());
}

删除操作依赖于 SiftDown:先把要删除的第 i 个元素与最后一个元素交换,然后收缩数组,再通过 SiftDown 把交换上来的元素向下调整。

void Delete(A& h, size_t i) {size_t n = h.size();if (n == 0 || i == 0 || i > n) {return;}if (i == n) {h.resize(n - 1);return;}std::swap(h[i - 1], h[n - 1]);h.resize(n - 1);SiftDown(h, n - 1, i);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/295444.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android之二维码生成和识别

二维码 1、ZXing库介绍 这里简单介绍一下ZXing库。ZXing是一个开放源码的,用Java实现的多种格式的1D/2D条码图像处理库,它包含了联系到其他语言的端口。Zxing可以实现使用手机的内置的摄像头完成条形码的扫描及解码。该项目可实现的条形码编码和解码。目前支持以下格式:UPC…

得罪前女友到底有多可怕?

1 多带一支笔到底有多重要&#xff08;via&#xff1a;100天用泰语撩到小哥哥&#xff09;▼2 专门为跪键盘而生&#xff08;via&#xff1a;in外设&#xff09;▼3 当表情包不再模糊▼4 新娘&#xff1a;他渣你&#xff0c;你干啥子整我&#xff01;&#xff01;&#xff0…

Windows 10 版本 21H2 正式发布

微软今日宣布开始推送 Windows 10 版本 21H2。 Windows 10 版本 21H2 将作为 Windows 10 2021 年 11 月更新向运行 Windows 10 版本 2004、Windows 10 版本 20H2 和 Windows 10 版本 21H1 的电脑推出。 为保证升级效果&#xff0c;Windows 10 版本 21H2 将进行分阶段和可评估的…

成功的换心手术——Windows Phone 8 发布

微软在刚刚开始的 Windows Phone 开发者峰会上正式发布了代号阿波罗的 Windows Phone 8 操作系统。虽然微软之前几乎没在公开场合提过阿波罗&#xff0c;但新系统变化的大方向早已泄漏。不过整个发布会依然充满惊喜&#xff0c;微软整整演示了一个多小时新功能&#xff0c;完全…

世上最“贵”的河:河里石头比黄金还值钱?甚至还有士兵驻守!

全世界只有3.14 % 的人关注了爆炸吧知识大家都听过世界上最长的河是尼罗河世界上最宽的河是亚马逊河但是你知道世界上最“贵”的河是哪条河吗这条河中最贵的原因不是水中有什么特殊生物也不是河里有黄金钻石仅仅是因为这里的“石头”这河里的石头可不普通大都是翡翠原石而这条河…

ASP.NET Core 跨平台图形验证码实现

概述几年前&#xff0c;大部分网站、论坛之类的是没有验证码的&#xff0c;因为对于一般用户来说验证码只是增加了用户的操作&#xff0c;降低了用户的体验。但是后来各种灌水机器人、投票机器人、恶意注册机器人层出不穷&#xff0c;大大增加了网站的负担同时也给网站数据库带…

日常管理

2019独角兽企业重金招聘Python工程师标准>>> 关于目前自己iOS项目使用的第三方开源库 1.AFNetworking 目前比较推荐的iOS网络请求组件&#xff0c;默认网络请求是异步&#xff0c;通过block回调的方式对返回数据进行处理。 2.FMDB 对sqlite数据库操作进行了封装&…

Android之android studio如何把项目分享到github并提修改的代码到Github

android studio如何提交代码到Github 第一步、下载git 第二步:在android studio指定git路径 File->Settings 测试Git是否可用

清华伯克利造出机械小强:承重200万倍踩不死,跑得和真蟑螂一样快

全世界只有3.14 % 的人关注了爆炸吧知识打不死的小强&#xff0c;真的是太讨厌了&#xff01;既然打不死&#xff0c;那……干脆仿造一个出来&#xff0c;为我所用&#xff0c;岂不是爽歪歪&#xff1f;还真有人仿造成功了&#xff0c;最新的一期Science Robotics&#xff0c;就…

RowVersion字段从SqlServer到PostgreSQL的迁移

SQL Server 有个列是rowversion&#xff0c;之前是timestamp&#xff0c;因此这两个关键字在SQL server中是同义词&#xff0c;不过目前timestamp处在废弃阶段&#xff0c;因此我们最好使用rowversion来代替它。而在数据库迁移时&#xff0c;因为使用到该类型&#xff0c;因此要…

支付宝手机支付

1 在 "商家服务"开通"移动支付" 2 创建应用

zk 08之:Curator之一:zk客户端Curator

Curator是Netflix公司开源的一个Zookeeper客户端&#xff0c;与Zookeeper提供的原生客户端相比&#xff0c;Curator的抽象层次更高&#xff0c;简化了Zookeeper客户端编程。 它包含以下几个组件&#xff1a; ComponentdescriptionRecipesImplementations of some of the common…

Android之调用微信登陆、分享、支付

转载&#xff1a;http://blog.csdn.net/lowprofile_coding/article/details/48086381 前言:用了微信sdk各种痛苦,感觉比qq sdk调用麻烦多了,回调过于麻烦,还必须要在指定包名下的actvity进行回调,所以我在这里写一篇博客,有这个需求的朋友可以借鉴一下,以后自己别的项目有用到…

两年发表14篇论文,其中10篇一作,这是她的科研进阶攻略

全世界只有3.14 % 的人关注了爆炸吧知识本文来源&#xff1a;浙江大学两年发表14篇论文&#xff0c;其中一作10篇&#xff0c;包括4篇Top SCI&#xff0c;2篇SCI和4篇EI&#xff1b;持有2项发明专利&#xff0c;出版1部英文专著&#xff0c;斩获2020年度学生学术十大新成果奖第…

生活在任务栏的猫, CPU使用率越高它就跑的越快

生活在任务栏的猫, CPU使用率越高它就跑的越快Runcat 是一个桌面软件, 这只猫会显示在您的任务栏上面, 它会一直奔跑, 它的运行速度取决于CPU的使用率, 支持 Windows 和 Mac 平台。您还可以用它查看系统资源使用率, 包括CPU使用率,内存,电池状态,网络传输速度等。如果这只猫一直…

在Ant的javac中指定源文件编码方式,以避免警告: 编码 GBK 的不可映射字符的错误...

为什么80%的码农都做不了架构师&#xff1f;>>> * 该错误会造成源文件中的字符串出现混乱&#xff0c;从而影响indexOf()之类函数的正常功能&#xff0e; <javac srcdir"${common.src.dir}" destdir"${build.temp.common.classes.dir}" de…

现在要吃软饭的,都这么明目张胆了吗?

1 加我一个&#xff1f;▼2 为什么电梯里会有两只光着的右脚&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼这样&#xff1f;3 老爹的脸更黑了▼4 但也不影响吧&#xff1f;都不及格▼5 妈&#xff0c;你说的一点都没错&#xff01;&#xff08;素材来源网络&…

Android之app引导页(背景图片切换加各个页面动画效果)

转载&#xff1a;http://blog.csdn.net/lowprofile_coding/article/details/48037095 先看效果图: 1.显示三个页面的Activity 用view pager去加载三个fragment实现,控制点点点的切换,监听view pager的切换,控制fragment动画的开始跟结束,重写了view pager,实现了背景图片的移…

VSCode SSH 连接提示: spawn UNKNOWN

随笔记录 目录 1. 背景介绍 2. 确认问题 : ssh -V 3. 解决问题 3.1 确认本地 ssh.exe 路径 3.2 修改vscode Remote.ssh:Path 3.2.1 设置 Reomte.ssh:Path - 方法一 3.2.2 设置 Reomte.ssh:Path - 方法二 1. 背景介绍 windows 系统vscode ssh remote CentOS7&#xff…

手把手教你学Dapr - 9. 可观测性

介绍通过Tracing(跟踪)、Metrics(指标)、Logs(日志)和Health(运行状况)监控应用程序。分布式跟踪Dapr 使用 Zipkin 协议进行分布式跟踪 和 Metrics 收集。由于 Zipkin 协议的普遍性&#xff0c;许多后端都是开箱即用的&#xff0c;例如 Stackdriver、Zipkin、New Relic 等。结合…