C/C++ 堆排序

news2024/11/18 9:47:06

个人主页:仍有未知等待探索-CSDN博客

专题分栏:数据结构_仍有未知等待探索的博客-CSDN博客

                                                      欢迎大家来指教!

一、前言

今天要介绍的是堆排序。

首先什么是堆?简而言之,堆就是二叉树的数组形式,用数组来存储二叉树。

这个堆和C语言中讲的堆区是不同的两个概念,不要混淆。

二、堆排序

堆排序的核心就是构建一个特殊的二叉树,这个二叉树的特性是:其父节点大于等于(小于等于)其左右孩子结点。

故,最终创建的二叉树的根节点会是该二叉树的最大值(最小值)。

那怎么才能让整个数组中的数据有序呢?那我们就让数组中第一个数和最后一个数进行交换,然后以第一个数和倒数第二个数为端点,继续进行构建堆,然后选出次最小值。以此类推~~~

我以大根堆为例(从数组下标为1的地方开始存储):

1、堆的数据类型

typedef int HpDataType;
typedef struct Heap
{
	HpDataType* a;//进行存储堆中的数据
	int size;//该堆的有效数据个数
	int capacity;//该堆中的容量
}Hp;

2、堆的初始化

//初始化
void Heapinit(Hp* php)
{
	assert(php);//断言,如果php为空的话,报错
	php -> a = NULL;
	php -> size = 0;
	php -> capacity = 0;
}

3、堆的销毁

//堆的销毁
void HeapDestory(Hp* php)
{
	assert(php);
	free(php -> a);//释放掉开辟的空间
	php -> a = NULL;//防止php -> a为野指针
	php -> size = 0;
	php -> capacity = 0;
}

4、堆的创建

// 堆的创建
// k为要插入到堆里面的元素
void Heap_push(Hp* php, int k)
{
	assert(php); //断言
	if (php -> size == php -> capacity)// 如果容量不够,扩容
	{
		// 如果容量为0,则给个初始值,否则就二倍扩容
		int size = php -> capacity == 0 ? 4 : php -> capacity * 2;
		Hp* tmp = (Hp* ) realloc(php -> a, (size + 1) * sizeof(int));
		if (tmp == NULL) 
		{
			perror("realloc failed");
			exit(-1);// 扩容失败则结束程序
		}

		php -> a = tmp;
		php -> capacity = size;	
	}

	// 插入到堆的最后
	php -> a[++ php -> size] = k;

	// 向上调整,如果比父节点要大就进行交换
	up(php -> a, php -> size);
}

 5、向上调整

// 向上调整
// idx为要调整元素的下标
void up(int* a, int idx)
{
	assert(a);
	int child = idx;// 该结点下标
	int parent = child / 2;// 该结点的父节点,我是从下标为1开始存的

	while (child != 1)
	{
		if (a[parent] < a[child])
		{
			Swap(&a[parent], &a[child]);
		}
		else
		{
			// 不需要调整,就退出
			break;
		}

		// 继续向上找
		child = child / 2;
		parent = parent / 2;
	}
}

 6、删除堆的最后一个元素

该操作的主要作用是将该堆中最大的元素进行归位,并且进行调整除了最后一个元素的二叉树的顺序,使其仍为一个堆。

// 进行删除最后一个元素
void Heappop(Hp* php)
{
	assert(php);
	assert(php -> size > 0);
	
	Swap(&php -> a[1], &php -> a[php -> size]);
	php -> size --;

	int parent = 1;
	int child = 2 * parent;

	while (child <= php -> size)
	{
		// 假设左节点最大,如果右结点比左节点大,就child存右节点下标
		if (child + 1 <= php -> size && php -> a[child + 1] > php -> a[child])
		{
			child += 1;
		}
		// 判断孩子结点和父节点的值
		if (php -> a[parent] < php -> a[child])
		{
			Swap(&php -> a[parent], &php -> a[child]);
		}
		else 
		{
			// 不需要则退出
			break;
		}

		parent = child;
		child = 2 * parent;

	}
}

 7、取出堆顶元素

// 取出堆顶元素
HpDataType Heaptop(Hp* php)
{
	assert(php);
	assert(php -> size > 0);

	return php -> a[1];
}

三、总代码及其运行结果

#define _CRT_SECURE_NO_WARNINGS  1

// 大根堆

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

typedef int HpDataType;
typedef struct Heap
{
	HpDataType* a;// 进行存储堆中的数据
	int size;// 该堆的有效数据个数
	int capacity;// 该堆中的容量
}Hp;

void Heapinit(Hp* php);// 初始化
void HeapDestory(Hp* php);// 堆的销毁
void Heap_push(Hp* php, int k);// 堆的创建
void Swap(int* a, int* b);// 交换
void up(int* a, int idx);// 向上调整
HpDataType Heaptop(Hp* php);// 取堆顶元素
void Heappop(Hp* php);// 删除堆的最后一个元素

int main()
{
	int a[] = {1, 2, 3, 4, 5, 6, 7};

	Hp hp;

	Heapinit(&hp);

	int len = sizeof(a) / sizeof(a[0]);
	for (int i = 0; i < len; i++ )
	{
		Heap_push(&hp, a[i]);
	}

	printf("初始堆:\n");
	for (int i = 1; i <= len; i ++ )
	{
		printf("%d ", hp.a[i]);
	}

	printf("\n有序序列:\n");

	while (hp.size > 0)
	{
		printf("%d ", Heaptop(&hp));

		Heappop(&hp);
	}

	HeapDestory(&hp);

	return 0;
}

// 初始化
void Heapinit(Hp* php)
{
	assert(php);// 断言,如果php为空的话,报错
	php -> a = NULL;
	php -> size = 0;
	php -> capacity = 0;
}

// 堆的销毁
void HeapDestory(Hp* php)
{
	assert(php);
	free(php -> a);// 释放掉开辟的空间
	php -> a = NULL;// 防止php -> a为野指针
	php -> size = 0;
	php -> capacity = 0;
}

void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

// 向上调整
// idx为要调整元素的下标
void up(int* a, int idx)
{
	assert(a);
	int child = idx;// 该结点下标
	int parent = child / 2;// 该结点的父节点,我是从下标为1开始存的

	while (child != 1)
	{
		if (a[parent] < a[child])
		{
			Swap(&a[parent], &a[child]);
		}
		else
		{
			// 不需要调整,就退出
			break;
		}

		// 继续向上找
		child = child / 2;
		parent = parent / 2;
	}
}

// 堆的创建
// k为要插入到堆里面的元素
void Heap_push(Hp* php, int k)
{
	assert(php); //断言
	if (php -> size == php -> capacity)// 如果容量不够,扩容
	{
		// 如果容量为0,则给个初始值,否则就二倍扩容
		int size = php -> capacity == 0 ? 4 : php -> capacity * 2;
		Hp* tmp = (Hp* ) realloc(php -> a, (size + 1) * sizeof(int));
		if (tmp == NULL) 
		{
			perror("realloc failed");
			exit(-1);// 扩容失败则结束程序
		}

		php -> a = tmp;
		php -> capacity = size;	
	}

	// 插入到堆的最后
	php -> a[++ php -> size] = k;

	// 向上调整,如果比父节点要大就进行交换
	up(php -> a, php -> size);
}

// 取出堆顶元素
HpDataType Heaptop(Hp* php)
{
	assert(php);
	assert(php -> size > 0);

	return php -> a[1];
}

// 进行删除最后一个元素
void Heappop(Hp* php)
{
	assert(php);
	assert(php -> size > 0);
	
	Swap(&php -> a[1], &php -> a[php -> size]);
	php -> size --;

	int parent = 1;
	int child = 2 * parent;

	while (child <= php -> size)
	{
		// 假设左节点最大,如果右结点比左节点大,就child存右节点下标
		if (child + 1 <= php -> size && php -> a[child + 1] > php -> a[child])
		{
			child += 1;
		}
		// 判断孩子结点和父节点的值
		if (php -> a[parent] < php -> a[child])
		{
			Swap(&php -> a[parent], &php -> a[child]);
		}
		else 
		{
			// 不需要则退出
			break;
		}

		parent = child;
		child = 2 * parent;

	}
}

 

谢谢大家! 

 

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

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

相关文章

【C++】类和对象之匿名对象友元内部类

目录 一、匿名对象 1、基础格式 2、使用场景 二、友元 1、友元函数 2、友元类 三、内部类 1、概念 2、特性 四、拷贝对象时的一些编译器优化 1、函数传参 2、对象返回 一、匿名对象 1、基础格式 【注意】 &#x1f7e2;匿名对象的声明周期只有当前行&#xff0c;进入…

推荐熊猫电竞赏金电竞系统源码

熊猫电竞赏金电竞系统源码&#xff0c;包含APP、H5和搭建视频教程&#xff0c;支持运营级搭建&#xff0c;这套源码是基于ThinkPHPUniaapp框架开发的。 系统是一套完整的电竞平台开发源码&#xff0c;包括赛事管理、用户系统、竞猜系统、支付系统等模块。源码结构清晰&#xff…

OpenGL排坑指南—贴图纹理绑定和使用

一、前言 在OpenGL学习 的纹理这一章中讲述了纹理贴图的使用方式&#xff0c;主要步骤是先创建一个纹理的对象&#xff0c;和创建顶点VAO类似&#xff0c;然后就开始绑定这个纹理&#xff0c;最后在循环中使用&#xff0c;有时候可能还要用到激活纹理单元的函数。然而&#xff…

【漏洞复现】先锋WEB燃气收费系统文件上传漏洞 1day

漏洞描述 /AjaxService/Upload.aspx 存在任意文件上传漏洞 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得利用网络从事危害国家安全、荣誉和利益,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作…

【算法】增减序列(贪心,差分)

题目 给定一个长度为 n 的数列 a1,a2,…,an&#xff0c;每次可以选择一个区间 [l,r]&#xff0c;使下标在这个区间内的数都加一或者都减一。 求至少需要多少次操作才能使数列中的所有数都一样&#xff0c;并求出在保证最少次数的前提下&#xff0c;最终得到的数列可能有多少种…

如何下载 DEM数字高程数据(SRTM和COPERNICUS)

数字高程模型&#xff08;Digital Elevation Model&#xff0c;DEM&#xff09;是地球表面的数字表示&#xff0c;以地形高程信息的形式存在。DEM通常以栅格或点云的形式存在&#xff0c;其中每个单元&#xff08;栅格或点&#xff09;都具有对应的高程数值。DEM可以使用各种技…

MYSQL篇--锁机制高频面试题

Mysql锁机制 1对mysql的锁有了解吗&#xff1f; 首先我们要知道&#xff0c;mysql的锁 其实是为了解决在并发事务时所导致的数据不一致问题的一种处理机制&#xff0c;也就是说 在事务的隔离级别实现中&#xff0c;就需要利用锁来解决幻读问题 然后我们可以聊到锁的分类 按锁…

Windows安装和使用kafka

一、安装kafka 由于kafka依赖jdk和zookeeper&#xff0c;安装kafka之前需要先安装jdk和zookeeper&#xff0c;也可以使用kafka自带的zookeeper。安装jdk可以参考&#xff1a;Windows和Linux安装jdk&#xff0c;此处使用kafka自带的zookeeper&#xff0c;不单独安装。 下面在Wi…

Python列表(list)

目录 列表列表的创建与删除访问列表元素index() 方法 列表的遍历添加&#xff0c;修改和删除列表元素添加修改删除 对列表统计和计算count() 方法如需确定列表中有**多少元素**&#xff0c;请使用 len() 方法&#xff1a;检查项目是否存在**复制列表****合并两个列表****list()…

Win10安装配置Redis,修改密码

一、下载Redis tporadowski 提供了 支持 Windows平台的 Redis 安装包&#xff0c;目前仍在维护&#xff0c;目前最新版本是 5.0.14&#xff0c;更新速度跟Redis官网也相差好几个大版本。 下载地址&#xff1a;https://github.com/tporadowski/redis/releases 二、Redis 安装 …

极客时间-如何降低用户鉴权的流量压力

背景 内容是极客时间-徐长龙老师的高并发系统实战课的个人学习笔记&#xff0c;欢迎大家学习&#xff01;https://time.geekbang.org/column/article/596644 使用Session方式实现用户的用户鉴权 优点 信息都在服务端储存&#xff0c;对客户端不暴露任何用户敏感的数据信息 缺…

SQL-修改表操作

目录 DDL-表操作-修改 添加字段 &#xff08;方括号内容可选&#xff09; 修改字段 修改指定字段的数据类型 修改字段名和字段类型 删除字段 修改表名 删除表 删除指定表&#xff0c;并重新创建该表 总结 &#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦…

Window平台安装MongoDB

在部署前需要在官网先确定系统对应的Mongo DB版本。 本机电脑为Window10&#xff0c;所以这里以MongoDB 6.0版本。 1 在官网下载安装包 2 安装MongoDB MongoDB Compass 是一个图形界面管理工具&#xff0c;如果勾选了安装会花费长一点时间&#xff0c;可以取消掉勾选&#xff…

[UI5] ODATA V4中的CRUD

文章目录 前言一、Read二、Create三、Update四、Delete 前言 ODATA V4在CRUD方面与V2截然不同。 这篇文章简单介绍V4中是如何进行CRUD操作 一、Read Model不再有read方法&#xff0c; 一般是把Path绑定到View中进行读取&#xff0c; 如果需要额外的读取数据&#xff0c;可使用…

树状结构查询 - 华为OD统一考试

OD统一考试 分值&#xff1a; 200分 题解&#xff1a; Java / Python / C 题目描述 通常使用多行的节点、父节点表示一棵树&#xff0c;比如&#xff1a; 西安 陕西 陕西 中国 江西 中国 中国 亚洲 泰国 亚洲 输入一个节点之后&#xff0c;请打印出来树中他的所有下层节点。 …

Python: Spire.PDF-for-Python

# encoding: utf-8 # 版权所有 2024 ©涂聚文有限公司 # 许可信息查看&#xff1a; # 描述&#xff1a; # Author : geovindu,Geovin Du 涂聚文. # IDE : PyCharm 2023.1 python 3.11 # Datetime : 2024/1/11 10:32 # User : geovindu # Product : PyChar…

TypeScript类型挑战:实现内置的Omit实用类型

掌握 TypeScript Omit 泛型&#xff0c;一起完成 Type 挑战&#xff0c;巩固 TypeScript 知识。 为了帮助读者更好地巩固 TypeScript 的知识&#xff0c;我从 Github 上的 type-challenges 库中选择了几十个挑战&#xff0c;与您一起完成类型挑战。 挑战 实现内置的 Omit&…

分布式系统架构设计之分布式消息队列的水平扩展性、安全可用性以及监控与调优

一、分布式消息队列的水平扩展 随着业务的快速发展和数据的不断增长&#xff0c;单一的消息队列服务器往往难以满足高并发、高可用和高吞吐量的需求&#xff0c;因此&#xff0c;如何实现消息队列的水平扩展成为了一个重要的问题。这部分我将从分区、副本、负载均衡等关键概念…

影响eCPM的因素有哪些?如何提升eCPM?

eCPM&#xff08;千次展示有效收益&#xff09;直接关系广告变现收益的高低&#xff0c;是开发者们最关心的数据之一。要想优化提升eCPM&#xff0c;首先要了解哪些主要因素影响eCPM&#xff0c;再针对性优化广告库存&#xff0c;提高变现收益。 https://www.shenshiads.com …

线性回归实例

1、线性回归&#xff08;linear Regression&#xff09;和逻辑回归&#xff08;logistic Regression&#xff09;的区别 线性回归主要是用来拟合数据&#xff0c;逻辑回归主要是用来区分数据&#xff0c;找到决策边界。 线性回归的代价函数常用平方误差函数&#xff0c;逻辑回…