深入浅出堆排序: 高效算法背后的原理与性能


在这里插入图片描述

🎬 鸽芷咕:个人主页

 🔥 个人专栏: 《linux深造日志》 《高效算法》

⛺️生活的理想,就是为了理想的生活!

📋 前言

  🌈堆排序一个基于二叉堆数据结构的排序算法,其稳定性和排序效率在八大排序中也是名列前茅。
  ⛳️堆我们已经讲解完毕了,今天就来深度了解一下堆排序是怎么实现的以及为什么他那么高效。
  📚本期文章收录在《数据结构&算法》,大家有兴趣可以看看呐
  ⛺️ 欢迎铁汁们 ✔️ 点赞 👍 收藏 ⭐留言 📝!

文章目录

  • 📋 前言
  • 一、堆排序的思想概念
  • 二、堆排序的两种实现方式
    • 2.1 向上取整
    • 2.2 向下取整
  • 三、堆排序的实现代码
    • 3.1 如何利用向上调整建堆
    • 3.1 如何利用向下调整建堆
    • 3.3 堆建完了如何排序数据
    • 3.4 堆排完整代码
  • 四、俩种实现方式的效率对比
    • 4.1 向上调整建堆时间复杂度计算
    • 4.2 向下调整建堆时间复杂度计算
    • 4.3 对比结果
    • 4.4 堆的时间复杂度计算
  • 📝文章结语:

一、堆排序的思想概念

堆排序可以说是排序算法中比较高效的了,既稳定又高效。既然叫堆排序那么肯定离不来堆,基于二叉树来进行构建:

  • 不知道大家发现过没有堆有一个特性
  • 要不就是最大值(大堆)要不然就是一个最小值(小堆)

而我们吧堆顶最大值或最小值进行 pop删除并取出每次的 最大值或者最小值把这些值存储起来

之后他的数据是不是也排序完了,而我们又是用数组来存储的删除不就是把下标 减减吗?

二、堆排序的两种实现方式

堆排序的核心思想就是利用堆的特性来进行数据的取出每次都是最大值或者最小值,那么我得到一组数据要进行堆排序首先:

  • 这组数据需要时堆才能进行排序,那么我们就要开始建堆就完了。

建堆的方法一共有俩种分别是向下取整和向上取整这里都给大家介绍一下

2.1 向上取整

向上取整就是,把新的数据尾插到堆里面然后把他和父节点进行对比调整:

  • 数组存储这里有一个特点 parent = (child-1)/ 2 ;
  • 父节点等于子节点 -1 除二
    在这里插入图片描述

📚 代码演示:

//向上调整
void adjustup(HeapTypeData* a, int child)
{
	int parent = (child - 1) / 2;

	while (child > 0)
	{
		//建小堆
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}

}

2.2 向下取整

向下取整的思想就是把堆顶数据左右子树的的数值进行对比然后向下进行调整:

  • 🔥 向下调整算法有一个前提:左右子树必须是一个堆,才能调整
  • 这里由于是数组存储的所以堆的左右子树都是
  • child = parent* 2+1;
  • 孩子节点的左节点都等于 父节点
    在这里插入图片描述
    如果堆顶数据和左右子树对比 ,然后再进行调整数据

📚 代码演示:

//向下调整
void adjustdown(HeapTypeData* a, int n, int parent)
{
	int child = parent* 2+1;

	while (child < n)
	{
		if (child+1 < n && a[child + 1] < a[child])
		{
			child++;
		}

		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent*2 +1;
		}
		else
		{
			break;
		}
	}
}

三、堆排序的实现代码

3.1 如何利用向上调整建堆

向上调整的思想大家都懂了,而建堆的话我们可以这样想:

  • 从数据的第一个数每次向上调整这样
  • 当调整到最后一个数的时候前面所有的都是已经调整好的堆

📚 代码演示:

//向上调整
void adjustup(HeapTypeData* a, int child)
{
	int parent = (child - 1) / 2;

	while (child > 0)
	{
		//建小堆
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}

}
//向上调整建堆  OR 建小堆降序 
//                建大堆升序
for (int i = 1; i < n; i++)
{
	adjustup(a, i);
}

3.1 如何利用向下调整建堆

利用向下调整建堆的要求是左右俩边都是堆才可以向下调整:

  • 那么我们可以把他分为 分治子问题 先向下调整左右子树的在一部部调整堆顶

  • 而堆的最后一个子树一定是堆

在这里插入图片描述

这样我们就可以利用数组存储堆的特性 父节点等于子节点 -1除2

  • parent = (child-1)/ 2 ;
  • 然后再利用循环 减减 把每个子树都调整完到堆顶堆就减好了

📚 代码演示:


//向下调整
void adjustdown(HeapTypeData* a, int n, int parent)
{
	int child = parent* 2+1;

	while (child < n)
	{
		if (child+1 < n && a[child + 1] > a[child])
		{
			child++;
		}

		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent*2 +1;
		}
		else
		{
			break;
		}
	}
}

//向上调整建堆  OR 建小堆降序 
//                建大堆升序
for (int i = (n-1-1)/2; i > 0; i--)
{
	adjustdown(a, n, i);
}

3.3 堆建完了如何排序数据

堆我们建完了,排序难道一个个把堆顶数据取出然后再放进去吗? 当然不是排序算法都是在数组的 原本空间上进行排序:

  • 我们的思想还是和删除 POP 一样先把堆顶的数据和堆底进行交换
  • 然后再利用下标减减删除数据,(虚拟删除其实还在)
  • 这样每次最大或者最小的数据都被按规律放在原空间里面了

📚 代码演示:

//开始排序
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		adjustdown(a, end, 0);
		end--;
	}

3.4 堆排完整代码

//向上调整
void adjustup(HeapTypeData* a, int child)
{
	int parent = (child - 1) / 2;

	while (child > 0)
	{
		//建小堆
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}

}
//向下调整
void adjustdown(HeapTypeData* a, int n, int parent)
{
	int child = parent* 2+1;

	while (child < n)
	{
		if (child+1 < n && a[child + 1] > a[child])
		{
			child++;
		}

		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent*2 +1;
		}
		else
		{
			break;
		}
	}
}

void HeapSort(int* a, int n)
{
	//向上调整建堆  OR 建小堆降序 
	//                建大堆升序
	/*for (int i = 1; i < n; i++)
	{
		adjustup(a, i);
	}*/

	for (int i = (n-1-1)/2; i > 0; i--)
	{
		adjustdown(a, n, i);
	}


	//开始排序
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		adjustdown(a, end, 0);
		end--;
	}
}

四、俩种实现方式的效率对比

4.1 向上调整建堆时间复杂度计算

在这里插入图片描述

4.2 向下调整建堆时间复杂度计算

在这里插入图片描述

4.3 对比结果

建堆思想时间复杂度
向上调整建堆O(N * logN)
向下调整建堆O(N)

🔥 所以我们在进行堆排序的时候一定首先选取向下调整算法时间复杂度更优。

  • 假设有1000万个数据
建堆思想排序次数
向上调整1000W*24(约等于 2亿多)
向下调整1000W

所以我们向下调整的算法是远远大于向上调整的这是为什么呢?

  • 🔥 因为 向下调整最后一层节点多且全部需要调整到第一层(调整h-1次)
  • 🔥 而向下调整 最后一层不需要调整,越是接近底层调整越少

在这里插入图片描述

4.4 堆的时间复杂度计算

在这里插入图片描述

📝文章结语:

☁️ 以上就是本章的全部内容了,各位铁汁们快去试试吧!
看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注

💛 💙 💜 ❤️ 💚💓 💗 💕 💞 💘 💖
拜托拜托这个真的很重要!
你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

浏览器开发者工具(Developer Tools)详解

作为一名前端开发人员&#xff0c;熟练应用浏览器开发工具很重要。笔者在这方面的知识未成体系&#xff0c;最近在跟着chorme官方文档学习&#xff0c;于是整理了本文&#xff0c;如有不足&#xff0c;欢迎指正。 目录 1.elements(元素) 2.console(控制台) 3.sources(源代码…

逻辑斯蒂回归-建模概率计算(鸢尾花)

导入的数据说明 因为气候不同&#xff0c;造就性不同&#xff0c;统计鸢尾花的关键特征数据&#xff1a;花萼长度、花萼宽度、花瓣长度&#xff0c;花瓣宽度 植物学家划分&#xff1a; setosa(中文名&#xff1a;山鸢尾) versicolor(中文名&#xff1a;杂色鸢尾) virginica(中…

React学习计划-React16--React基础(三)收集表单数据、高阶函数柯里化、类的复习

1. 收集表单数据 包含表单的组件分类 受控组件——页面中所有输入类的DOM,随着输入&#xff0c;把值存维护在状态里&#xff0c;需要用的时候去状态里取值&#xff08;推荐&#xff0c;避免了过渡使用ref&#xff09;非受控组件——页面中所有输入类的DOM&#xff0c;现用现取…

高级算法设计与分析(五) -- 回溯法

系列文章目录 高级算法设计与分析&#xff08;一&#xff09; -- 算法引论 高级算法设计与分析&#xff08;二&#xff09; -- 递归与分治策略 高级算法设计与分析&#xff08;三&#xff09; -- 动态规划 高级算法设计与分析&#xff08;四&#xff09; -- 贪心算法 高级…

LED电子屏幕正迎来人屏互动技术

随着科技的不断进步&#xff0c;LED电子屏幕正迎来人屏互动技术的未来。传统LED电子屏幕一直以来只是作为显示器&#xff0c;实现单向传播&#xff0c;缺乏人群互动和观众参与的乐趣。然而&#xff0c;随着LED显示屏厂家技术的不断创新&#xff0c;LED电子屏幕正在摆脱单向传播…

C++基础语法总结

C使用 C的源文件扩展名是&#xff1a;cppC程序的入口是main函数C完全兼容c语言的语法 1、cin、cout C中常使用cin、cout进行控制台的输入和输出 #include <iostream> using namespace std;int main() {cout << "hello world !!!" << endl;retu…

如何设计更优雅的 React 组件?

在日常开发中&#xff0c;团队中每个人组织代码的方式不尽相同。下面我们就从代码结构的角度来看看如何组织一个更加优雅的 React 组件&#xff01; 1. 导入依赖项 我们通常会在组件文件顶部导入组件所需的依赖项。对于不同类别的依赖项&#xff0c;建议对它们进行分组&#…

Django(二)

1.django框架 1.1 安装 pip install django3.21.2 命令行 创建项目 cd 指定目录 django-admin startproject 项目名mysite ├── manage.py [项目的管理工具] └── mysite├── __init__.py├── settings.py 【配置文件&#xff0c;只有一部分…

在Portainer创建Nginx容器并部署Web静态站点实现公网访问

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《Linux》《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;…

大数据---34.HBase数据结构

一、HBase简介 HBase是一个开源的、分布式的、版本化的NoSQL数据库&#xff08;即非关系型数据库&#xff09;&#xff0c;依托Hadoop分布式文件系统HDFS提供分布式数据存储&#xff0c;利用MapReduce来处理海量数据&#xff0c;用Zookeeper作为其分布式协同服务&#xff0c;一…

虚拟机安装CentOS7并配置共享文件夹

一、虚拟机安装CentOS7并配置共享文件夹 二、CentOS 7 上hadoop伪分布式搭建全流程完整教程 三、本机使用python操作hdfs搭建及常见问题 四、mapreduce搭建 五、mapper-reducer编程搭建 六、hive数据仓库安装 一、虚拟机安装二、centos系统安装三、共享文件夹配置 一、虚拟机安…

C#学习笔记 - C#基础知识 - C#从入门到放弃 - C# 方法

C# 入门基础知识 - 方法 第8节 方法8.1 C# 函数/方法简介8.2 方法的声明及调用8.2.1 参数列表方法的声明及调用8.2.2 参数数组方法的声明及调用8.2.3、引用参数与值参数 8.3 静态方法和实例方法8.3.1 静态、实例方法的区别8.2.3 静态、实例方法的声明及其调用 8.4 虚方法8.4.1 …

“智”绘出海新航道,亚马逊云科技携手涂鸦智能助力智能家居企业全球化

随着人工智能、5G等技术的快速发展&#xff0c;智能家居行业呈现高速发展的态势。Statista数据显示&#xff0c;2022年全球智能家居行业支出总值为1145亿美元&#xff0c;欧美地区以较早的智能家居普及率&#xff0c;率先进入全屋智能时代&#xff0c;其中欧盟区国家家用智能设…

大规模采用奇点临近?Web3应用爆发离不开这个“支撑”赛道

作者&#xff5c;Jason Jiang 数据是当今世界最具价值的资源&#xff0c;也是数字掘金的必争之地。尽管Web3迄今仍有诸多争议&#xff0c;但随着铭文、Gamefi、DeFi等链上生态的多样化发展&#xff0c;我们正身处Web3应用爆发的洪流之中&#xff0c;区块链数据赛道也因此备受关…

LLM微调(四)| 微调Llama 2实现Text-to-SQL,并使用LlamaIndex在数据库上进行推理

Llama 2是开源LLM发展的一个巨大里程碑。最大模型及其经过微调的变体位居Hugging Face Open LLM排行榜&#xff08;https://huggingface.co/spaces/HuggingFaceH4/open_llm_leaderboard&#xff09;前列。多个基准测试表明&#xff0c;就性能而言&#xff0c;它正在接近GPT-3.5…

vue 简单实现购物车:商品基础信息最终的 html 文件 + 商品计数器的组件处理,实现了购物车;

购物车实现过程&#xff1a; Ⅰ、商品购物车作业需求&#xff1a;1、商品购物车页面示例&#xff1a;2、具体需求&#xff1a; Ⅱ、html 文件的构建&#xff1a;商品购物车.html Ⅲ、组件文件的构建&#xff1a;商品购物车1.js Ⅳ、小结&#xff1a; Ⅰ、商品购物车作业需求&am…

TypeScript实战——ChatGPT前端自适应手机端,PC端

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 可以在线体验哦&#xff1a;体验地址 文章目录 前言引言先看效果PC端手机端 实现原理解释 包的架构目录 引言 ChatGPT是由OpenAI开发的一种基于语言模型的对话系统。它是GPT&#xff08;…

Java智慧工地源码 SAAS智慧工地源码 智慧工地管理可视化平台源码 带移动APP

一、系统主要功能介绍 系统功能介绍&#xff1a; 【项目人员管理】 1. 项目管理&#xff1a;项目名称、施工单位名称、项目地址、项目地址、总造价、总面积、施工准可证、开工日期、计划竣工日期、项目状态等。 2. 人员信息管理&#xff1a;支持身份证及人脸信息采集&#…

重学设计模式-Iterator(迭代器模式)

Iterator迭代器模式 介绍&#xff1a; 迭代器模式是一种行为型设计模式&#xff0c;它允许你在不暴露集合底层表示&#xff08;并不知道集合底层使用何种方式对数据尽心存储&#xff09;的情况下遍历集合中的元素。 这种模式提供了一种方法&#xff0c;可以顺序访问一个聚合…

Leetcod面试经典150题刷题记录 —— 矩阵篇

矩阵篇 1. 有效的数独2. 螺旋矩阵Python 3. 旋转图像Python额外开辟数组空间原地置换法 4. 矩阵置零5. 生命游戏Python 1. 有效的数独 题目链接&#xff1a;有效的数独 - leetcode 题目描述&#xff1a; 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 &#xff0c;验…