数据结构——堆的应用 堆排序详解

news2024/11/23 19:39:58

💞💞 前言

hello hello~ ,这里是大耳朵土土垚~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹
在这里插入图片描述

💥个人主页:大耳朵土土垚的博客
💥 所属专栏:数据结构学习笔记
💥对于数据结构顺序表、链表、堆有疑问的都可以在上面数据结构的专栏进行学习哦~
有问题可以写在评论区或者私信我哦~

在土土的上篇博客二叉树堆的介绍与实现中,我们发现测试代码是升序;今天我们就来分析堆的重要应用——**堆排序**🎉🎉。
升序实现如下:

#include"Heap.h"
int main()
{
	Heap hp;
	HeapInit(&hp);
	int a[] = { 65,100,70,32,50,60 };
	for (int i = 0; i < 6; i++)
	{
		HeapPush(&hp, a[i]);
	}
	while (!HeapEmpty(&hp))
	{
		int top = HeapTop(&hp);
		printf("%d\n", top);
		HeapPop(&hp);
	}
    HeapDestroy(&hp);
	return 0;
}

在这里插入图片描述
详情可在土土的博客数据结构——lesson7二叉树堆的介绍与实现中查看🥳🥳

一、堆排序(基础版)

既然是堆排序,那我们首先肯定得有一个堆,这里土土就可以偷个懒将上篇博客中实现的堆代码copy一下🥰🥰

堆的实现

#include"Heap.h"
//堆的初始化
void HeapInit(Heap* hp)
{
	assert(hp);
	hp->a = NULL;
	hp->capacity = 0;
	hp->size = 0;
}
// 堆的销毁
void HeapDestroy(Heap* hp)
{
	assert(hp);
	free(hp->a);
	hp->a = NULL;
	hp->capacity = 0;
	hp->size = 0;
}
//交换函数
void Swap(HPDataType* a,HPDataType* b)
{
	HPDataType tmp = *a;
	*a = *b;
	*b = tmp;
}

//堆向下调整算法
void AdjustDown(HPDataType* a, int n,int parent)
{
	//找到较小的孩子节点
	int child = parent * 2 + 1;
	
	//向下调整
	while (child < n)
	{
		if (child + 1 < n && a[child] > a[child + 1])
		{
			child++;
		}
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = child * 2 + 1;
		}
		else
			break;
		
	}
}

//向上调整
void AdjustUp(HPDataType* a,int child)
{
	//找到双亲节点
	int parent = (child - 1) / 2;
	//向上调整
	while (child > 0)
	{
		if (a[parent] > a[child])
		{
			Swap(&a[parent], &a[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			break;
		
	}
}
// 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);
	//判断容量
	if (hp->size == hp->capacity)//容量满了扩容
	{
		int newcapacity = hp->capacity == 0 ? 0 : 2 * hp->capacity;
		HPDataType* new = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newcapacity);
		if (new == NULL)
		{
			perror("realloc fail");
			return;
		}
		hp->a = new;
		hp->capacity = newcapacity;
	}
	//尾插
	hp->a[hp->size] = x;
	hp->size++;
	//向上调整算法
	AdjustUp(hp->a,hp->size-1);
}
// 堆的删除,删除堆顶元素
void HeapPop(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	Swap(&hp->a[0], &hp->a[hp->size - 1]);
	hp->size--;
	//向下调整算法
	AdjustDown(hp->a, hp->size, 0);

}
// 取堆顶的数据
HPDataType HeapTop(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	return hp->a[0];
}
// 堆的数据个数
int HeapSize(Heap* hp)
{
	assert(hp);
	return hp->size;

}
// 堆的判空
int HeapEmpty(Heap* hp)
{
	assert(hp);
	return hp->size == 0;
}

当然在使用这些函数时要记得先声明一下,这里我们都放到一个头文件Heap.h中

Heap.h

#pragma once
#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;//数组空间
}Heap;
//以下是实现堆的函数
// 堆的初始化
void HeapInit(Heap* hp);
// 堆的销毁
void HeapDestroy(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);


使用时只需包含该头文件即可
#include"Heap.h"

堆排序

给定一个数组a[ ] = {7,8,3,5,1,9,5,4},我们需要利用上面的堆来将它进行排序

🤩🤩思路
①我们首先需要将数组中的元素插入堆中(利用HeapPush函数),
💫前面我们已经学习过堆插入函数,它里面利用堆向上调整算法会自动将插入的数据调整为一个堆(我们实现的是小堆);
②然后我们需要获取堆顶元素(也就是小堆中最小的元素),利用HeapTop函数即可;
③获取最小元素后我们就需要获取次小元素,先利用堆的删除函数(HeapPop函数),将堆顶元素(也就是小堆中最小的元素)删除;
💞删除函数中堆向下调整算法又会将剩余元素调整为小堆,此时堆顶元素就是删除一个元素后最小的元素;
④将删除后的元素重新拷贝回数组a中;
⑤循环②③两步直到全部排序成功。

代码实现如下:

 #include"Heap.h"
void HeapSort(int* a,int size)
{
	Heap hp;
	HeapInit(&hp);
	//将a中元素插入堆中
	for (int i = 0; i < size; i++)
	{
		HeapPush(&hp, a[i]);
	}
	//获取堆顶(最小)元素并删除
	int i = 0;
	while (i < size)
	{
		a[i++] = HeapTop(&hp);
		HeapPop(&hp);
	}
	HeapDestroy(&hp);
}
int main()
{
	int a[] = { 7,8,3,5,1,9,5,4 };
	int size = sizeof(a) / sizeof(int);
	HeapSort(a,size);
	return 0;
}

🥳🥳结果如下:
排序前
在这里插入图片描述
排序后
在这里插入图片描述

💥💥上述堆排序的实现尽管能够实现排序,但是…我们发现如果没有提前实现堆或者准备好堆的代码,我们是没办法实现的,而且我们需要来回拷贝数据,空间复杂度较大。
🥰🥰这里就需要介绍下面简便版堆排序啦~

二、堆排序(简便版)

在土土的数据结构学习笔记数据结构——lesson7二叉树堆的介绍与实现中,详细介绍了堆向上调整算法与堆向下调整算法,接下来我们就可以利用这两个函数来实现堆以及堆的排序🥳🥳

(1)利用堆向上或向下调整算法实现堆

堆向上调整算法实现

//向上调整算法
void AdjustUp(HPDataType* a,int child)
{
	//找到双亲节点
	int parent = (child - 1) / 2;
	//向上调整
	while (child > 0)
	{
		if (a[parent] > a[child])
		{
			Swap(&a[parent], &a[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			break;
		
	}
}

数组a[ ] = {7,8,3,5,1,9,5,4},我们可以看成一个二叉树:
在这里插入图片描述
只需要从第二个数8开始每次读取一个数据都向上调整为堆,那么读完整个数组就可以得到一个堆啦~🥰🥰
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//从第二个数据开始向上调整建堆
for (int i = 1; i < size; i++)
{
	AdjustUp(a, i);
}

🤩🤩之前基础版排序是又开辟了一个空间来存放a中的数据,排成堆后又每次选取最小的元素拷贝回a中,不仅麻烦而且会增加空间的使用;
所以简便版排序便直接将a看成一个二叉树利用向上调整算法直接成堆,不需要开辟额外的空间。

堆向下调整算法实现

🥰🥰类似于向上调整算法的实现,所不同的是开始调整的位置不再从第二个数开始,而是从最后一个非叶子节点开始向下调整:
在这里插入图片描述
调整完了再依次往前找到前一个非叶子节点(下标是元素个数size-2再除2)重复向下调整即可;
🥳🥳使用向下调整的时间复杂度较向上调整小,所以我们这里选择用向下调整

代码如下:

//堆向下调整算法
for (int i = (size-2 )/ 2 ; i >= 0; i--)
{
	AdjustDown(a, size, i);
}

结果如下:
在这里插入图片描述
在这里插入图片描述

可以发现已经将其调整为一个小堆了🥳🥳

(2)利用堆向下调整算法排序

那我们应该怎么将堆中的元素排序呢?
🥳🥳这就要利用堆向下调整算法了

思路🥳🥳

①交换首尾元素,将堆中最小的元素(首元素)换到尾部;
②将交换后的尾部元素忽略,剩余元素利用堆向下调整算法(除头外左右子树都是堆)调整为堆;
在这里插入图片描述
③重复②直到全部排完,得到降序数组:
在这里插入图片描述

代码如下:

//排序
int end = size-1;//堆底元素下标
while (end)
{
	Swap(&a[0], &a[end]);
	AdjustDown(a, end, 0);
	end--;
}

🤩🤩Swap函数在这里:

//交换函数
void Swap(HPDataType* a, HPDataType* b)
{
	HPDataType tmp = *a;
	*a = *b;
	*b = tmp;
}

(3)完整实现🥳🥳

void HeapSort(int* a,int size)
{
	//堆向下调整算法
	 for (int i = (size-1 )/ 2 ; i >= 0; i--)
	{
		AdjustDown(a, size, i);
	}
	
	//排序
	int end = size-1;//堆底元素下标
	while (end)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}

}
int main()
{
	int a[] = { 7,8,3,5,1,9,5,4 };
	HeapSort(a, 8);

	return 0;
}

结果如下:
在这里插入图片描述

✨✨思考:如果我们要排升序应该利用什么堆呢?相信大家通过上面的学习与理解都知道应该用大堆对不对?具体代码大家可以参考上面小堆实现降序来自己试着写一写哦~

三、结语

以上就是堆的应用——堆排序啦~,我们发现可以不用写堆的实现代码就可以将一个数组排成堆🥳🥳,关键在于堆向上调整与向下调整算法的理解与运用,大家都学废了吗 ,💞💞 完结撒花 ~🎉🎉🎉

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

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

相关文章

[Prob] Definition 3.7.5 (Function of two r.v.s)

定义3.7.5&#xff08;两个随机变量的函数&#xff09;&#xff1a;给定一个样本空间 \( S \) 的实验&#xff0c;如果 \( X \) 和 \( Y \) 是映射 到X(s) 和 Y(s) 的随机变量&#xff0c;那么 g(X, Y) 就是映射 s 到 g(X(s), Y(s)) 的随机变量。 请注意&#xff0c;我…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:DatePicker)

日期选择器组件&#xff0c;用于根据指定日期范围创建日期滑动选择器。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 DatePicker(options?: DatePickerOptions) 根据指定范…

SAP 批量删除工艺路线和主配方_简介

通常我们在创建工艺路线的时候或者在导入工艺路线的时候,会存在数据导入出错,或者你创建的工艺路线需要删除的情况,通常情况下我们第一个想到的就是使用CA02或者C202去删除工艺路线或者是主配方。但是这样会存在一个问题就是,首先我们知道工艺路线和主配方都是存在在组里面…

记一次项目所学(中间件等)-动态提醒功能(RocketMQ)

记一次项目所学(中间件等&#xff09;–动态提醒功能&#xff08;RocketMQ&#xff09; 订阅发布模式与观察者模式 RocketMQ&#xff1a;纯java编写的开源消息中间件 高性能低延迟分布式事务 Redis : 高性能缓存工具&#xff0c;数据存储在内存中&#xff0c;读写速度非常快 …

VBA_NZ系列工具NZ03:利用右键进行筛选操作

我的教程一共九套及VBA汉英手册一部&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到数据库&#xff0c;到字典&#xff0c;到高级的网抓及类的应用。大家在学习的过程中可能会存在困惑&#xff0c;这么多知识点该如何组织…

2024西安天文科技与探索装备展览会-相聚7月

2024西安天文科技与探索装备展览会-相聚7月 时间&#xff1a;2024年7月14-16日 地点&#xff1a;西安国际会展中心 首个面向全球天文产业的展览&#xff1b;中国唯一全面反映天文产业链的盛会&#xff1b; 定位于国际高端产业的展会&#xff1b;众多天文机构鼎力支持和重点培…

HTTPS网络请求失败WiFi请求成功

在xml的config文件中添加raw文件位置 raw文件是证书的pem文件去掉key文件 文件名称去掉多余的.cn

炫云客户端12载风华,最初界面竟长这样?满满都是回忆!

2013年&#xff0c;注定是一个意义非凡的节点 让我们将时间轴拨回到2013年 这一年&#xff0c;到底发生了什么呢&#xff1f; 这一年&#xff0c;嫦娥三号成功落月 中国探月工程开启新征程 这一年&#xff0c;工信部向三大运营商颁发4G牌照 标志着我国正式迈入4G时代 同…

51单片机基础篇系列-超声波测距

&#x1f308;个人主页&#xff1a;会编辑的果子君 &#x1f4ab;个人格言:“成为自己未来的主人~” HC-SR04产品特点 典型工作用电压&#xff1a;5V 超小静态工作电流&#xff1a;小于2mA 感应角度&#xff1a;不大于15度 探测距离&#xff1a;2cm-400cm 高精度&#…

Spring AOP常见面试题

目录 一、对于AOP的理解 二、Spring是如何实现AOP的 1、execution表达式 2、annotation 3、基于Spring API&#xff0c;通过xml配置的方式。 4、基于代理实现 三、Spring AOP的实现原理 四、Spring是如何选择使用哪种动态代理 1、Spring Framework 2、Spring Boot 五…

【C++】STL(四) deque容器

4、deque容器 4.1 简介 ① 功能&#xff1a;双端数组&#xff0c;可以对头端进行插入删除操作&#xff0c;也可以对尾端进行插入和删除操作。 ② deque与vector区别&#xff1a; vector对于头部的插入效率低&#xff0c;数据量越大&#xff0c;效率越低&#xff0c;例如头部…

掘根宝典之C++迭代器简介

在C中&#xff0c;容器是一种用于存储和管理数据的数据结构。C标准库提供了多种容器&#xff0c;每种容器都有其独特的特点和适用场景。 我们知道啊&#xff0c;我们可以通过下标运算符来对容器内的元素进行访问&#xff0c;但是只有少数几种容器才同时支持下标运算符&#xf…

#车载诊断协议DoIP系列 —— 套接字处理 在线检查

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师(Wechat:gongkenan2013)。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 本就是小人物,输了就是输了,不要在意别人怎么看自己。江湖一碗茶,喝完再挣扎,出门靠自己,四海皆为家。人生的面吃一…

Java中 final、finally、finalize 有什么区别?

1、典型回答 final、finally、finalize 是 Java 中三个不同的关键字&#xff0c;它们除了长得像之外&#xff0c;其他的&#xff08;作用和含义&#xff09;完全不同。 它们三个的区别就好像&#xff1a;雷、雷锋、雷峰塔之间的区别。&#xff08;是三个完全不同的东西&#…

【动态规划】C++算法312 戳气球

作者推荐 视频算法专题 本文涉及知识点 动态规划汇总 LeetCode312 戳气球 有 n 个气球&#xff0c;编号为0 到 n - 1&#xff0c;每个气球上都标有一个数字&#xff0c;这些数字存在数组 nums 中。 现在要求你戳破所有的气球。戳破第 i 个气球&#xff0c;你可以获得 nums…

基于springboot+vue实现教学改革项目管理系统项目【项目源码+论文说明】计算机毕业设计

基于springbootvue实现教学改革项目管理系统演示 摘要 教学改革行业的不断发展才能让更多的学生受益&#xff0c;那么教学改革的老师们对于教改可谓是花费了很大的心血。这种心血不仅仅在教学方式方法上&#xff0c;而且还是在于线下的流程审批和审核及教学改革的项目资料整理…

便携式隧道能见度仪的使用

TH-BN6随着交通基础设施的不断发展&#xff0c;隧道作为连接城市、山区等关键地段的交通要道&#xff0c;发挥着越来越重要的作用。然而&#xff0c;隧道内的能见度问题一直是困扰隧道运营者的难题。为了保障隧道通行安全&#xff0c;便携式隧道能见度仪应运而生。 二、便携式隧…

解忧杂货铺(①):必备网站资源

目录 在线工具 简单教程 程序员导航网 中国大学 青柠 廖雪峰的官方网站 在线工具 各类工具 https://tool.lu/ 简单教程 简单教程 https://www.twle.cn/ 程序员导航网 程序员导航网 https://hao.panziye.com/ 中国大学 中国大学MOOC网 https://www.icourse163.org/ 青…

Python 对Excel工作表中的数据进行排序

在Excel中&#xff0c;排序是整理数据的一种重要方式&#xff0c;它可以让你更好地理解数据&#xff0c;并为进一步的分析和报告做好准备。本文将介绍如何使用第三方库Spire.XLS for Python通过Python来对Excel中的数据进行排序。包含以下三种排序方法示例&#xff1a; 按数值…