排序算法—堆排序

文章目录

  • 堆排序
    • 思路过程
      • 建堆
      • 排序
    • 代码实现

堆排序

时间复杂度:O(N*logN)
稳定性:不稳定(相同元素排序后的相对位置改变)

堆的逻辑结构是一棵完全二叉树;堆的物理结构是一个数组,通过下标表示父子结点的关系

这个数组的元素是按照层序遍历(广度优先)的方式存储的。

以下左图为例,其堆的数组元素为 { 6 ,8,10,12,14,16,18 }

【算法】小白的算法笔记:堆排序 (,,• ₃ •,,) - 知乎

以下parent、child、leftchild、rightchild均为下标值 (这些公式表示的是父子结点的关系)

  • parent = (child - 1) / 2
  • leftchild = parent * 2 + 1
  • rightchild = parent * 2 + 2

堆的两个特性:

1. 结构性: 用数组表示一棵完全二叉树
2. 有序性: 任一结点的关键字是其子树所有节点的最大值(或最小值)。
“最大堆”,也称“大顶堆”(或“大根堆”),简称大堆。特点是:父亲大于等于孩子
“最小堆”,也称“小顶堆”(或“小根堆”),简称小堆。特点是:父亲小于等于孩子

了解到这,考虑一个问题,假设有一个深度为4的小堆,那么深度为2的那一层的每一个数据一定都小于深度为3的那一层的每一个元素吗?

不一定,堆的特性规定,只要每个父亲大于(或小于)自己的孩子即可,不需要大于(或小于)同层父亲的孩子。

思路过程

建堆

向下调整算法

前提:左右子树必须同为大堆或小堆
结果:将对象插入到二叉树中,使得插入后的二叉树满足堆的特性

以小堆为例, 父亲跟它的左右孩子的较小值比较,如果父亲比较小值大,那么交换较小孩子和父亲,父亲的值不变位置变化,然后继续和现在位置的左右孩子的较小值比较,父亲大继续交换,直到叶子节点终止(这是算法终止的第一种情况)。如果在到达叶子节点前,出现父亲比左右孩子较小值还要小的情况,那么不执行交换,终止算法执行(第二种终止情况)。
在这里插入图片描述

以大堆为例, 父亲跟它的左右孩子的较大值比较,如果父亲比较大值小,那么交换较大孩子和父亲,父亲的值不变位置变化,然后继续和现在位置的左右孩子的较大值比较,父亲小继续交换,直到叶子节点终止(这是算法终止的第一种情况)。如果在到达叶子节点前,出现父亲比左右孩子较大值还要大的情况,那么不执行交换,终止算法执行(第二种终止情况)。

如果逻辑上的完全二叉树不满足前提条件(根节点开始),怎么办?

既然对根节点没法使用向下调整算法,我们不妨从树的其他满足前提条件的结点开始使用向下调整算法。

从哪些结点开始使用向下调整算法?从叶子结点?

叶子节点没有左右孩子,那么默认就是一个小堆(或大堆),没必要对它使用向下调整算法。

因此,我们开始使用向下调整算法的结点是倒数第一个非叶子节点,然后将下标减1,就是倒数第二个非叶子节点,以此类推,结果建堆成功。

怎么找到倒数第一个非叶子结点?

堆数组最后一个元素是逻辑完全二叉树的最右边的叶子节点,它的父亲就是倒数第一个非叶子结点。

最右边的叶子节点的在数组中的下标是len - 1(len是数组的元素个数),要找它的父亲,用到父子结点关系公式:parent = (child - 1) / 2

代入得到倒数第一个非叶子节点对应的下标值:(len - 1 - 1) / 2

排序

建堆的过程了解了,假如我要排升序,那么我该建大堆还是小堆?

第一反应就是建小堆,最小数在堆顶,堆的物理结构是数组,当我们把第一个元素(建小堆后第一个元素是序列中的最小元素)拿出来后,之后需要在剩下的数中再去选数,但是剩下的树的结构都乱了,需要重新建堆才能选出下一个数,建堆的时间复杂度为O(N),这样反复建堆可以实现排序的目的,不过堆排序就失去了效率优势。

所以我们排升序需要建大堆,最大数在堆顶,每次取堆顶元素,与末尾元素交换,比如:某个序列建大堆后数组元素顺序为:{ 9,8,6,7,3,2,1,5,4,0 },9是堆顶元素,将堆顶元素取出来与0元素交换,得到{ 0,8,6,7,3,2,1,5,4,9 },9是最大元素,不需要再参与排序,除去9后前 len - 1 个元素可以执行向下调整算法,0的左右子树都是大堆,算法执行完毕后,次大的元素到达堆顶,将次大的元素与倒数第二个元素交换,此时倒数一二个元素已经不需要参与排序,以此类推,便实现了顺序堆排序。

代码实现

void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}//每趟向下调整算法
void AdjustDown(int* a, int len, int root)
{//一次向下调整算法int parent = root;//假设左孩子大于右孩子,child存储值较大的孩子的下标int child = parent * 2 + 1;while (child < len){if (child + 1 < len && a[child] < a[child + 1])//语句child + 1 < n用于应对某个父亲只有左孩子的情况{child++;}if (a[child] > a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}//堆排序
void HeapSort(int* a, int len)
{//实现顺序,建大堆for (int i = (len - 1 - 1) / 2; i >= 0; i--){AdjustDown(a, len, i);}//排序int j = len - 1;while (j > 0){Swap(&a[0], &a[j]);AdjustDown(a, j, 0);--j;}}

我们测试一下:

void TestHeapSort()
{int a[] = { 2,4,6,4,1,1,8,4,2,0 };int len = sizeof(a) / sizeof(int);HeapSort(a, len);Print(a, len);
}
int main()
{TestHeapSort();return 0;
}

在这里插入图片描述


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

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

相关文章

Shell脚本学习(一):Shell内置命令与Shell运算符

Shell内置命令 理解内置命令的含义。 内置命令介绍 Shell内置命令&#xff0c;就是由Bash Shell自身提供的命令&#xff0c;而不是文件系统中的可执行文件。 使用type 可以用来确定一个命令是否是内置命令&#xff1a; type 命令演示&#xff1a; 对于上述演示的两个命令来…

普乐蛙VR神州飞船设备VR太空舱体验馆VR博物馆

中国航天式浪漫知多少&#xff1f;千百年来古人对浩瀚宇宙有着无尽的浪漫想象&#xff0c;而在一代又一代中国航天事业奋斗者的努力中&#xff0c;远古神话不再是幻想&#xff0c;它终被照进现实——中国载人飞船“神舟”、中国载人空间站“天宫”、中国绕月人造卫星“嫦娥一号…

mybatis的一对多

业务&#xff1a;通常主表从表 查询&#xff0c;一对多关系&#xff0c;通常是先查主表&#xff0c;然后拿主表的 关联字段与从表关联。在代码中 通常用for 循环等方法给 从表的数据赋值&#xff0c;很麻烦&#xff0c;&#xff0c;&#xff0c;很麻烦。。。。 用mybatis的…

Linux 正则表达式及三剑客(awk、grep、sed)

原文&#xff1a;https://blog.iyatt.com/?p14465 1 前言 2024.4.8 昨天接了一个代写单子&#xff0c;要求用 Python 实现 sed 的部分功能。我用 Linux 也有几年了&#xff0c;基本的命令知道&#xff0c;但是几乎没有写涉及高级功能的 shell 脚本&#xff0c;所以对于正则表…

小米汽车值得去吗?最终拒了 offer。

车企选择 今天逛某职场 App 时&#xff0c;无意间看到一篇寻求 offer 抉择意见的帖子&#xff1a; 这位同学刚从加班闻名&#xff08;但 CEO 强调既学华为狼性&#xff0c;也学华为分配&#xff09;的理想汽车离职。 经过了 6 轮面试&#xff0c;收到了小米 offer&#xff0c;但…

QT文本操作

文本的操作 文本的读写流程 文本的读写流程 // 文本的读写流程// 1.打开文件》打到文本》QFileDialog::getOpenFileName返回的是// 一个字符串&#xff0c;包括了路径文件名字// open()方法打开文本// 2.读写文本// readAll(),readLine(),write()// 3.关闭文本 // close()使用…

2024年文化、历史与人文艺术与社会发展国际会议(CHHASD2024)

2024年文化、历史与人文艺术与社会发展国际会议(CHHASD2024) 会议简介 2024年国际文化、历史、人文、艺术与社会发展会议&#xff08;CHHASD2024&#xff09;将在中国武汉举行&#xff0c;主题为“文化、历史&#xff0c;人文、艺术和社会发展”。CHHASD2024汇集了来自世界各…

postman接口测试(入门到精通)

下载&#xff1a; postman官方地址 测试外部接口&#xff1a;测试被测系统和外部系统之间的接口。&#xff08;只需要测试正例即可&#xff09; 测试内部接口&#xff1a; 1.内部接口只提供给内部系统使用。&#xff08;只需要测试正例即可&#xff09; 2.内部接口提供给外…

0.25W 3KVDC 隔离单双输出DC/DC SMD型电源模块 ——TPAT-W2 系列

TPAT-W2系列是一款标准的表面贴装电源模块&#xff0c;完全实现采用全自动贴片机来组装和满足回流焊工艺&#xff0c;大大提高产能和人工费用&#xff0c;提供正负双输出&#xff0c;工业级环境温度。此系列产品小巧&#xff0c;效率高&#xff0c;低输出纹波及提供3000V以上的…

Eland上传bge-large-zh-v1.5向量化模型到ElasticSearch中

最近需要做一些向量检索&#xff0c;试试ES 一、准备 系统&#xff1a;MacOS 14.3.1 ElasticSearch&#xff1a;8.13.2 Kibana&#xff1a;8.13.2 本地单机环境&#xff0c;无集群&#xff0c;也不基于Docker BGE是一个常见的文本转向量的模型&#xff0c;在很多大模型RAG应…

模型预测控制MPC(2)—— 无约束线性MPC

前文&#xff1a;模型预测控制MPC&#xff08;1&#xff09;—— 基础概念参考&#xff1a;模型预测控制&#xff08;2022春&#xff09;本文从偏控制的角度介绍无约束线性MPC方法&#xff0c; x , u , J x,u,J x,u,J 分别代表状态、动作和代价函数 文章目录 1. 问题定义1.1 多…

低代码集成Java系列:高效构建自定义插件

前言 随着软件开发的快速发展和需求的不断增长&#xff0c;开发人员面临着更多的压力和挑战。传统的开发方法需要花费大量的时间和精力&#xff0c;而低代码开发平台的出现为开发人员提供了一种更加高效、快速的开发方式。今天小编就以构建命令插件为例&#xff0c;展示如何使…