八大算法排序@堆排序(C语言版本)

news2024/7/7 16:58:24

目录

  • 堆排序
    • 大堆排序
      • 概念
      • 算法思想
        • 建堆
        • 建堆核心算法
        • 建堆的代码
      • 排序代码实现
    • 小堆排序
      • 代码实现
      • 时间复杂度
      • 空间复杂度

堆排序

  堆排序借用的是堆的特性来实现排序功能的。大堆需要满足父节点大于子节点,因此堆顶是整个数组中的最大元素。小堆则相反,要求父节点小于子节点,因此父节点是整个数组中最小元素。
借助这一特性,对于大堆,我们可以将堆顶的元素,和堆的最后一个元素置换,这样便将最大的数排到了最后面。同时将堆顶的有效个数减1。但是置换上来堆顶的元素,也就破坏了堆的结构,但是堆顶元素的左右子节点依旧是堆的结构,因此可以对堆顶进行调整,然其继续满足堆的特性。这样便找出第二大的元素,继续重复上述动作,便能将大的数组都往后挪动。待到只剩下一个有效数据时,便拍好了数组。大家认为这是一个升序的数组呢?还是一个降序的数组呢?
  答案显而易见,使用大堆进行排序,结果是升序的效果。同样的思路,使用小堆进行排序,结果是降序的结果。

因此,得出一个结论:
想要对数组进行升序的排序,使用大堆;
想要对数组进行降序的排序,使用小堆;



大堆排序

概念

  大堆概念:每个节点(父节点)的值都大于或等于其子节点的值。
大堆排序借用的便是大堆的特性,将堆顶的元素和堆的最后一个元素进行置换,置换上来堆顶的数据再进行堆结构的调整。然后重复以上动作,实现排序功能。



算法思想

要想实现大堆排序,首先需要对数组建立起大堆的结构。下面我们使用数组 arr[] = { 6 , 4 , 3 , 9 , 2 , 1 ,5 ,7 ,8 };来模拟演示大堆的构建,以及排序的大体过程。如下是数组的初始状态:

数组初始状态

首先,让我们来看看堆的结构是如何产生的。

建堆

  首先,要明确一点的就是:堆是基于完全二叉树而演变出来的一种数据结构。分为最大堆和最小堆两种类型,但它们都是完全二叉树。完全二叉树结构特点如下:

完全二叉树
注意:完全二叉树每个节点必须是从左到右依次遍布的,中间不能跳跃,如第四层的第三个节点,必须是第三层第二个节点的左节点,否则将不满足完全二叉树的特点。

  了解以上知识后,还得须知建堆的核心算法是 “向下调整算法” 。通过算法的一步步调整,一步步建立起堆这一结构。但是!该算法有个前提,就是调整的节点的左右子节点必须是堆的结构。
  有同学可能有疑惑,这不是扯淡吗?我就是要建立的堆结构,又要我满足调整的节点的左右子节点也是堆结构,闹着玩儿呢?
  并不是的,正所谓没有条件,便创造出条件。我们可以从下往上进行建堆啊!比如回到原数组 arr[] = { 6 , 4 , 3 , 9 , 2 , 1 ,5 ,7 ,8 };我们先对数组模拟出堆的结构出来:

原数组

肉眼可见的,原数组模拟出来的完全二叉树,并不符合堆的结构特点。那么如何做到前面所说的,创造出前提条件,进行建堆呢?从堆顶向下进行调整是不可能的,因为堆顶的元素6不满足核心算法的前提条件。但是!我们可以对元素9进行调堆啊,元素9不就正好满足核心算法的前提条件嘛。
首先元素9有左右两个子节点7和8。元素7和元素8左右子节点都为空节点,那么元素7和元素8可以认为是堆的结构,因此元素9左右子节点都是堆结构这一前提条件便满足了。可以使用建堆的核心算法,从元素9开始依次往上建堆。
  元素9建成堆结构后,下标减1。开始对元素3进行建堆,同样的元素3也满足核心算法的前提条件,同样可以使用核心算法进行堆结构的建立。接着就是元素4,元素4此时也满足了左右子节点都是堆结构的条件,同样进行堆的调节建立,最终是元素6也就是堆顶的调节,而这时堆顶也满足了左右子节点都是堆结构的前提条件,同样利用 “向下调整算法” 进行堆结构的建立。至此完成整个数组的堆结构构建。

注:
是如何找到要从哪一个节点进行堆的构建的?利用子节点找父节点的方法!如数组有n个元素,最后一个元素的下标则为n-1,根据完全二叉树的特点,最后一个元素的父节点的下标为:(n-1-1)/ 2。




建堆核心算法
// 两数交换
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}


// 向下调整算法
/*
传入参数:
ret:需要建立的数组
root:需要开始调节的节点
n:数组的个数
*/
void AdjustDown(int* ret,int n,int root)
{
	
	int parent = root;// 父节点,也就是要调节的节点
	int child = parent*2+1;// 子节点/孩子,默认是左节点

	while(child<n)
	{
		// 找出左右孩子,最大的孩子,同时要考虑只有左孩子的情况
		if(child+1<n && ret[child+1] > child[child])
		{
			child++;// 如果右孩子比左孩子大,则存储右孩子的下标
		}
		// 如果 子节点大于父节点,则子节点向上进行调整
		if(ret[parent] < ret[child])
		{
			swap(ret[parent],ret[child]);
			parent = child;
			child=parent*2+1;
		}
		else  // 父节点大于子节点则退出循环(注意父节点左右子节点都是堆的结构)
		{
			break;
		}
	}
}




建堆的代码
// 升序 - 建大堆
void CreateHeap(int* a, int n)
{
	// 建大堆,从最后一个节点的父节点开始建立
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDowm(a, n, i);
	}
}

建完堆后的数组如下所示:

建堆完成

至此,我们实现了对数组建堆这一个数据结构的条件。接下来便能对其进行排序了。



排序代码实现

void SortHeap(int* a,int n)
{
	// 排序
	for (int i = 1; i < n; i++)
	{
		Swap(&a[0], &a[n - i]);	// 将堆顶元素与堆的最后一个元素交换
		AdjustDowm(a, n  - i, 0); // 交换上堆顶的元素进行堆的调节,使其依旧保持大堆的结构特性
	}

}

至此,便借用大堆的特性,实现了升序的排序效果。








小堆排序

概念和算法思想和大堆差不多,便不过多冗余介绍了,下面直接给出小堆排序的实现的降序排序代码。




代码实现

// 两数交换
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}



// 向下调整算法 - 前提:左右子树都是堆
void AdjustDowm(int* a, int n, int root)
{
	int parent = root;
	int child = parent * 2 + 1;//默认左孩子
	
	while (child < n)
	{
		// 找出左右子节点中,最小的子节点
		if (child + 1 < n && a[child + 1] < a[child])
		{
			child++;
		}
		// 如果父节点比子节点大,则将子节点向上调整
		if (a[parent] > a[child])
		{
			Swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;	// 退出循环
		}

	}
}

// 降序 - 建小堆
void HeapSort(int* a, int n)
{
	// 建大堆
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDowm(a, n, i);
	}

	//PrintArr(a,n);//查看建堆后的数组

	// 排序
	for (int i = 1; i < n; i++)
	{
		Swap(&a[0], &a[n - i]);
		AdjustDowm(a, n  - i, 0);
	}

}

与大堆的差别仅改变了核心算法中的两处地方,如下:
在这里插入图片描述

下面介绍一下堆排序的时间复杂度和空间复杂度。




时间复杂度

O(N*logN)
大体计算如下:
建堆时间复杂度为O(N);
堆排序的时间复杂度为O(N*logN),因为将堆顶元素置换以后,还需要进行调堆,调堆的时间复杂度为O(logN)。每排一个元素需要进行一次调堆,所以排完整个数组的时间复杂度为O(N*logN)。
所以总的时间复杂度为:O(N+N*logN)。两者记录较大的,所以时间复杂度为O(N*logN)。




空间复杂度

O(1);

堆排序过程中,都是在原数组上进行的,没有申请空间,所以空间复杂度为O(1)。

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

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

相关文章

在Android设备上设置和使用隧道代理HTTP

随着互联网的深入发展&#xff0c;网络信息的传递已经成为人们日常生活中不可或缺的一部分。对于我们中国人来说&#xff0c;由于某些特殊的原因&#xff0c;访问国外网站时常常会遇到限制。为了解决这个问题&#xff0c;使用代理服务器成为了许多人的选择。而在Android设备上设…

WPD小波包理解

WPD是分析信号特性和提供具有时频局部化函数的正交小波基的有效工具&#xff0c;同时也是一种小波分解&#xff0c;它可以将原始信号分解成若干子层。小波分解实现了单侧分解&#xff0c;但它只分离频率的低通分量。相比之下&#xff0c;WPD提供了更精确的信号分析&#xff0c;…

40道java集合面试题含答案(很全)

1. 什么是集合 集合就是一个放数据的容器&#xff0c;准确的说是放数据对象引用的容器集合类存放的都是对象的引用&#xff0c;而不是对象的本身集合类型主要有3种&#xff1a;set(集&#xff09;、list(列表&#xff09;和map(映射)。 2. 集合的特点 集合的特点主要有如下两…

成为比开发硬气的测试人,我都经历了什么?

我的职业生涯很简单&#xff0c;可以说&#xff0c;我的测试生涯就是我的职业生涯。 大学的专业是计算机&#xff0c;当年是热门的学科&#xff0c;但自己的计算机知识不强悍&#xff0c;又加上学校的硬核是金融业&#xff0c;来学校校招的都是各大银行&#xff0c;且都是需要…

shopee利润怎么算?看妙手ERP如何帮您精准掌握店铺利润明细!

最近&#xff0c;妙手收到不少卖家朋友反应&#xff1a;“每个月对账的时候&#xff0c;常常遇到店铺利润明细不准确的情况。明明利润上显示是赚钱的&#xff0c;但是实际计算后发现店铺是亏损的&#xff0c;却找不到具体原因。”其实&#xff0c;究其根本就是&#xff1a;店铺…

2024货运市场继续回暖,满帮有望抓牢成长主旋律

2023年&#xff0c;物流货运行业将对它“刻骨铭心”。首次告别过去三年的特殊波动&#xff0c;物流货运的每一条细分赛道&#xff0c;都在努力跑出新速度&#xff0c;力图加速行业的修复&#xff0c;并走向高质量发展。中国物流与采购联合会在12月底指出&#xff0c;2023年物流…

ASP.NET Core基础之图片文件(一)-WebApi访问静态图片

阅读本文你的收获&#xff1a; 学会在WebApi项目中访问静态图片了解静态文件中间件UseStaticFiles的用法 系统中免不了要去处理图片文件&#xff0c;比如上传商品的图片、显示商品的图片&#xff0c;访问系统中的图片等等&#xff0c;根据微软官网描述&#xff1a; 静态文件&a…

运维人员的逆袭:IT界的“万金油”如何迈向人工智能时代

一、运维人员的角色变迁 1、从“修理工”到“系统守护者” 在传统的IT环境中&#xff0c;运维人员的主要任务是维护服务器、网络设备和数据库等基础设施。他们需要对硬件设备进行定期检查和维修&#xff0c;以确保系统的稳定运行。随着云计算和虚拟化技术的普及&#xff0c;运…

认真学SQL——MySQL入门之DQL多表查询

多表查询 本质: 把多个表通过主外键关联关系连接(join)合并成一个大表,再去查询 知识点&#xff1a; 外键 foreign key 外键概念: 在从表(多方)创建一个字段&#xff0c;引用主表(一方)的主键,对应的这个字段就是外键。 外键特点&#xff1a; 1:从表外键的值是对主表主键…

mysql 单表 操作 最大条数验证 以及优化

1、背景 开车的多年老司机&#xff0c;是不是经常听到过&#xff0c;“mysql 单表最好不要超过 2000w”,“单表超过 2000w 就要考虑数据迁移了”&#xff0c;“你这个表数据都马上要到 2000w 了&#xff0c;难怪查询速度慢”。 2、实验 实验一把看看… 建一张表 CREATE TABL…

【论文+在线运行】AnyText:能准确写汉字的AI绘图工具

源码&#xff1a;https://github.com/tyxsspa/AnyText 阿里在线运行: https://modelscope.cn/studios/damo/studio_anytext/summary 论文&#xff1a;2311.AnyText: Multilingual Visual Text Generation And Editing 一、AnyTexT是什么&#xff1f; 是一个基于扩散模型的&am…

四、HTML 属性

属性是 HTML 元素提供的附加信息。 一、HTML 属性 HTML 元素可以设置属性属性可以在元素中添加附加信息属性一般描述于开始标签属性总是以名称/值对的形式出现&#xff0c;比如&#xff1a;name"value"。 二、 属性实例 HTML 链接由 <a> 标签定义。链接的地…

用户管理第2节课 -- idea 2023.2 创建表--鱼皮

二、【先确定idea版本&鱼皮是否一致&#xff0c;再决定看不看这行】建表 2.1 idea 里连接数据库&#xff0c;通过可视化建表 2.1.1 清空表中数据 的 命令 truncate 清空 2.1.2 先输入删除表&#xff0c;的命令&#xff0c;再选中这行命令&#xff0c;执行&#xff0c;…

中科驭数鄢贵海新年演讲:数字经济下的算力基础先行,DPU自主创新力量大有可为

近日&#xff0c;中科驭数创始人、CEO鄢贵海受邀在北京电视台《金融街午餐会》新年特别活动中发表新年演讲。 鄢贵海在新年演讲中提到&#xff0c;在21世纪头30年&#xff0c;我们不可思议地经历了三次重要的科技变革&#xff0c;分别是互联网的普及、移动互联网的崛起、以及人…

Linux安装rabbitMq RPM安装 以及带延迟插件

rabbitmq安装 文档中rabbitmq下载链接 以及延迟插件 网盘下载 目前下载文件中版本已经过多个服务器安装测试 完全成功 1.安装执行 rpm -ivh openssl-libs-1.0.2k-19.el7.x86_64.rpm --force --nodeps rpm -ivh libnsl-2.34-28.el9_0.x86_64.rpm --force --nodeps rpm -ivh e…

打造高效会员卡营销策划方案,提升门店业绩

在激烈的行业竞争中&#xff0c;如何有效提升店铺的业绩&#xff0c;提高客户粘性和消费频次呢&#xff1f;答案可能就在你手中——那就是有效的会员卡营销策略。下面给大家探讨如何设计会员卡营销策划方案&#xff0c;从而增加客户的忠诚度&#xff0c;并推动销售增长。以目前…

element-ui Tree 树形控件 过滤保留子级并获取过滤后的数据

本示例基于vue2 element-ui element-ui 的官网demo是只保留到过滤值一级的&#xff0c;并不会保留其子级 目标 1、Tree 树形控件 保留过滤值的子级 2、在第一次过滤数据的基础上进行第二次过滤 先看效果 Tree 树形控件 保留过滤值的子级 <el-treeclass"filter-t…

希亦、RUUFFY、鲸立内衣洗衣机怎么样?爆款对比谁是巅峰榜首

内衣洗衣机是最近这两年兴起的一款家庭小型电器&#xff0c;现在很多小伙伴都注重到卫生健康问题了&#xff0c;然而市面上有着太多三无产品的内衣洗衣机&#xff0c;不仅很难把内衣裤清洗干净还很容易出现漏电的可能&#xff0c;所以这种内衣洗衣机真的存着很危险的隐患&#…

Rockchip平台Android应用预安装功能(基于Android13)

Rockchip平台Android应用预安装功能(基于Android13) 1. 预安装应用类型 Android上的应用预安装功能&#xff0c;主要是指配置产品时&#xff0c;根据厂商要求&#xff0c;将事先准备好的第三方应用预置进Android系统。预安装分为以下几种类型&#xff1a; 安装不可卸载应用安…

阿里云服务器8080端口怎么打开?在安全组中设置

阿里云服务器8080端口开放在安全组中放行&#xff0c;Tomcat默认使用8080端口&#xff0c;8080端口也用于www代理服务&#xff0c;阿腾云atengyun.com以8080端口为例来详细说下阿里云服务器8080端口开启教程教程&#xff1a; 阿里云服务器8080端口开启教程 阿里云服务器8080端…