[排序算法]4. 图解堆排序及其代码实现

先来看看什么是堆?           

        堆是一种图的树形结构,被用于实现“优先队列”(priority queues)

注:优先队列是一种数据结构,可以自由添加数据,但取出数据时要从最小值开始按顺序取出。

        在堆的树形结构中,各个顶点被称为“结点”(node),数据就存储在这些结点中,堆中的每个结点最多有两个子结点

        在堆中存储数据时必须遵守这样一条规则:子结点必定大于父结点。因此,最小值被存储在顶端的根结点中。从堆中取出数据时,取出的是最上面的数据。这样,堆中就能始终保持最上面的数据最小。同时由于最上面的数据被取出,因此堆的结构也需要重新调整。


01 首先,在堆中存储所有的数据,并按降序来构建堆。


02 现在,所有数据都存进堆里了。为了排序,需要再从堆中把数据一个个取出来。

注:
从降序排列的堆中取出数据时会从最大的数据开始取,所以将取出的数据反序输出,排序就完成了。


03 首先取出根结点的数字7。


04 重新构造堆。

重构的规则请参考:

[数据结构]图解堆结构及其代码实现-CSDN博客

05 同样地,取出根结点的数字6,将它放在右数第2个位置上。


06 重新构造堆。


07 重复上述操作直到堆变空为止。


08 排序中……


09 从堆中取出了所有数字,排序完成。

解说
        堆排序一开始需要将n个数据存进堆里,所需时间为O(nlogn)。排序过程中,堆从空堆的状态开始,逐渐被数据填满。由于堆的高度小于log2n,所以插入1个数据所需要的时间为O(logn)。
每轮取出最大的数据并重构堆所需要的时间为O(logn)。由于总共有n轮,所以重构后排序的时间也是O(nlogn)。因此,整体来看堆排序的时间复杂度为O(nlogn)。
        这样来看,堆排序的运行时间比之前讲到的冒泡排序、选择排序、插入排序的时间O(n2)都要短,但由于要使用堆这个相对复杂的数据结构,所以实现起来也较为困难。
        一般来说,需要排序的数据都存储在数组中。这次我们使用了堆这种数据结构,但实际上,这也相当于将堆嵌入到包含了序列的数组中,然后在数组中通过交换数据来进行排序。具体来说,就是让堆中的各结点和数组像下图这样呈对应关系。正如大家所见,这可以说是强行在数组中使用了堆结构。

 

代码演示

def heapify(arr, n, i):# 初始化最大值的索引为当前节点largest = i# 左子节点的索引left = 2 * i + 1# 右子节点的索引right = 2 * i + 2# 如果左子节点比当前节点大,更新最大值索引if left < n and arr[i] < arr[left]:largest = left# 如果右子节点比当前节点大,更新最大值索引if right < n and arr[largest] < arr[right]:largest = right# 如果最大值不是当前节点,交换值并递归调整if largest != i:arr[i], arr[largest] = arr[largest], arr[i]heapify(arr, n, largest)def heap_sort(arr):n = len(arr)# 构建最大堆(Max Heap)# 从最后一个非叶子节点开始到根节点,逐个进行堆调整for i in range(n // 2 - 1, -1, -1):heapify(arr, n, i)# 逐步取出最大元素并进行堆调整for i in range(n - 1, 0, -1):# 将当前最大值(堆顶)与未排序部分的最后一个值交换arr[i], arr[0] = arr[0], arr[i]# 对剩余元素重新构建最大堆heapify(arr, i, 0)return arrarr = [2, 6, 7,8, 9, 4, 10, 3, 5, 1]
sorted_arr = heap_sort(arr)
print(sorted_arr)
结果:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

更详细的可以看这篇文章:

[数据结构]图解堆结构及其代码实现-CSDN博客


文章来源:书籍《我的第一本算法书》

书籍链接:

我的第一本算法书 (豆瓣) (douban.com)

作者:宫崎修一 石田保辉

出版社:人民邮电出版社

ISBN:9787115495242

本篇文章仅用于学习和研究目的,不会用于任何商业用途。引用书籍《我的第一本算法书》的内容旨在分享知识和启发思考,尊重原著作者宫崎修一和石田保辉的知识产权。如有侵权或者版权纠纷,请及时联系作者。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/jhghuhbb/article/details/139089141

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

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

相关文章

vscode的使用 ubuntu入门之二十二

高亮标识符&#xff0c;变量或者函数可以用 rainbow-highlighter 这个插件 Press shiftaltz, and variables curser is on will be highlighted. Press the same command again to remove highlights. Press shiftalta to remove all highlights. 参考&#xff1a; 在VSCode…

【台阶问题】

目录 问题&#xff1a; 思路&#xff1a; 回溯-分支限界法 知识点 目标函数&#xff08;分支结束的情况&#xff09;: n0 约束函数&#xff08;截断不合理的分支&#xff09;: num < 2 、 i > n-i && num 0 限界函数&#xff08;阶段不最优的分支&#xf…

开发问题合集(待补充)

docker 学习文档 ollama 官网 streamlit 官方文档

通过伪造NPU设备,让AscendSpeed在没有安装torch_npu的环境中跑起来

通过伪造NPU设备,让AscendSpeed在没有安装torch_npu的环境中跑起来 代码输出 背景: 我想在GPU上运行AscendSpeed框架,因为没有torch_npu、deepspeed_npu,又不想一个个注释掉 方法: 1.本文本通过创建一个FakeDevice 类来伪造 NPU&#xff08;Neural Processing Unit&#xff0…

C语言动态内存分配

有些情况下需要开辟的空间大小在程序运行过程中才能确定下来&#xff0c;而常规的在栈区开辟空间是在编译时就分配好了内存&#xff0c;并且内存大小不能改变&#xff0c;因此需要引入动态内存分配&#xff0c;动态内存分配的内存是在堆区&#xff0c;需要由用户手动开辟&#…

共享内存的分享

共享内存是一种进程间通信&#xff08;IPC&#xff0c;Inter-Process Communication&#xff09;的机制&#xff0c;用于在不同进程之间共享数据。它允许多个进程访问同一个内存段&#xff0c;从而使数据传递更加高效。共享内存的主要作用包括&#xff1a; 1. 高效数据交换 共…

java: 警告: 源发行版 8 需要目标发行版 8

前言 该文章中项目背景是&#xff1a;IDEA与设置的版本与实际电脑配置的不一致。也就是说只改了这个团队项目的JDK版本&#xff0c;IDEA上其它项目JDK版本未更改。 提示&#xff1a; IDEA警告&#xff1a;javaX&#xff1a;警告&#xff1a;源发行版 需要目标发行版 简略步…

.NET 某和OA办公系统全局绕过漏洞分析

转自先知社区 作者&#xff1a;dot.Net安全矩阵 原文链接&#xff1a;.NET 某和OA办公系统全局绕过漏洞分析 - 先知社区 0x01 前言 某和OA协同办公管理系统C6软件共有20多个应用模块&#xff0c;160多个应用子模块&#xff0c;从功能型的协同办公平台上升到管理型协同管理平…

qt 笔记

外部进程嵌入到Qt进程界面 将外部进程嵌入到 Qt 进程的界面中是一项复杂的任务&#xff0c;因为它涉及到操作系统特定的细节。在不同的操作系统上&#xff0c;这种嵌入方式可能会有所不同。以下是一些可能的方法和步骤&#xff0c;针对常见操作系统&#xff08;如 Windows 和 …

JS-06 原型式继承借用构造函数实现继承

目录 1 原型式继承 场景 前置问题 实现方法 2 借用构造函数实现继承 前置问题 错误的实现方式 正确的实现方式 1 原型式继承 场景 a、创建一个纯洁的对象&#xff1a;对象在控制台打印什么属性都没有 b、创建一个继承自某个父对象的子对象 前置问题 一个对象里有很…

基于稀疏辅助信号平滑的心电信号降噪方法(Matlab R2021B)

基于形态成分分析理论&#xff08;MCA&#xff09;的稀疏辅助信号分解方法是由信号的形态多样性来分解信号中添加性的混合信号成分&#xff0c;它最早被应用在图像处理领域&#xff0c;后来被引入到一维信号的处理中。 在基于MCA稀疏辅助的信号分析模型中&#xff0c;总变差方…

Python面试宝典:Python中与爬虫基础以及数据抓取和解析相关的面试笔试题(1000加面试笔试题助你轻松捕获大厂Offer)

Python面试宝典:1000加python面试题助你轻松捕获大厂Offer【第二部分:Python高级特性:第十七章:Python爬虫:第一节:爬虫基础以及数据抓取和解析】 第十七章:Python爬虫第一节:爬虫基础以及数据抓取和解析1. HTTP协议2. HTML/CSS/JavaScript3. 解析库4. Web开发者工具5.…

Python - list (append, extend, split)

Python list 与 Java 数组 Python list使用[]包裹&#xff0c;类似于Java 数组。不同点在于Python list元素可以是任意类型&#xff0c;Java 数组元组只能是基本数据类型之一。 >>> a [a, 2, [1,2]] >>> type(a) <class list> Python 声明了列表a&…

【前端篇】前端开发大厂面试真题

为助力小伙伴们梳理前端知识体系&#xff0c;从而能够充分地做好面试准备&#xff0c;那么今天就来给大家分享一份前端开发的面试真题与相关知识点&#xff0c;其中涵盖了最新版本的八股文&#xff08;包含最新的 Vue 3 面试题&#xff09;、高频算法题以及大佬的面经&#xff…

嵌入式进阶——EEPROM读写

&#x1f3ac; 秋野酱&#xff1a;《个人主页》 &#x1f525; 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 设置EEPROM读写String字符串官方示例 EEPROM是一种可擦写可编程只读存储器&#xff08;Electrically Erasable Programmable Read-…

[ARM-2D 专题] arm-2d项目简介

Arm-2D 是一个用于 Cortex-M 处理器上的 2.5D 图像处理的开源项目。 由ARM公司开发和维护&#xff0c;属于官方性质的项目&#xff0c;目前最新版本为V1.1.6. 2022年7月发布1.0的预览版&#xff0c; 2023年2月发布第一个正式版V1.1.1&#xff0c;近一年来快速迭代和功能增强&a…

数据库-SQL优化下

Group by 优化 limit优化 count优化 UPDATE优化 当用id去更改的时候,一个是id1 另一个id2 这是没毛病的&#xff0c;因为加的是行锁 但是如果根据name 一个是where nameaa. 另一个是namehh,在另一个事务没提交前&#xff0c;另一个不能提交&#xff0c;因为name字段不是索引…

数据结构复习指导之B树和B+树

目录 B树和B树 考纲内容 1.B树及其基本操作 1.1B树的查找 1.2B树的高度&#xff08;磁盘存取次数&#xff09; 1.3B树的插入 1.4B树的删除 2.B树的基本概念 B树和B树 考纲内容 考研大纲对 B树和 B树的要求各不相同&#xff0c;重点在于考查B树&#xff0c;不仅要求理解…

基于51单片机数字频率计的设计

本文提出设计数字频率计的方案,重点介绍以单片机AT89C51为控制核心,实现频率测量的数字频率设计。测频的基本原理是采用在低频段直接测频法,在低频段直接测频法的设计思路,硬件部分由单片机和数计显示电路组成;软件部分由信号频率测量模块和数据显示模块等模块实现。应用单…

Raven2掠夺者2渡鸦2账号需要验证怎么解决 超简单验证账号教程

《渡鸦2》是一款源自韩国的创新力作&#xff0c;作为《Raven》系列的最新续篇&#xff0c;这款游戏在MMORPG手游领域内再度扩展了其标志性的暗黑奇幻宇宙&#xff0c;融入了大量革新的游戏设计与丰富内容。定档于2024年5月29日开启公测的《渡鸦2》&#xff0c;正处在紧张刺激的…