当前位置: 首页 > article >正文

【数据结构】_堆排序问题

目录

1. 基于建堆的堆排序(需调HPPush)

2. 基于原数组的堆排序(仅调AdjustUp)

3. 堆排序建堆与升降序的总结


在专栏前文中,已经实现了堆的基本结构与相关方法;(小根堆)

详见下文:

【数据结构】_堆的实现-CSDN博客文章浏览阅读429次,点赞19次,收藏8次。专栏前文中,已经介绍了入堆及向上调整算法,出堆及向下调整算法,详情见下文:实际对于整除运算parent = (child - 1) / 2,parent并不会小于0,但当parent=0时,若a[parent] > a[child],则再次进行child与parent的更新,使得parent与child均为0。当parent=0时,若若a[parent] > a[child],则再次进行child与parent的更新,使得child更新为0,下次则无法进入while循环;https://blog.csdn.net/m0_63299495/article/details/145521676https://blog.csdn.net/m0_63299495/article/details/145521676修改向上调整与向下调整的比较逻辑,将堆修改为大根堆

1. 基于建堆的堆排序(需调HPPush)

当前堆为大根堆,通过对数组元素逐个push进行建堆,再使用HPPop依次出堆顶元素,最终实现对原数组元素的降序排序:

#define _CRT_SECURE_NO_WARNINGS
#include"Heap.h"
void TestHeap1() {
	int a[] = { 4,2,8,1,5,6,9,7 };
	HP hp;
	HPInit(&hp);
	// 输出原始数组
	printf("orignal array: \n");
	for (size_t i = 0; i < sizeof(a) / sizeof(a[0]); i++) {
		printf("%d ", a[i]);
	}
	printf("\n");
	// 建立小根堆,输出其顺序存储
	for (size_t i = 0; i < sizeof(a) / sizeof(a[0]); i++) {
		HPPush(&hp, a[i]);
	}
	printf("minHeap array: \n");
	HPPrint(&hp);
	// 对原始数组使用堆排序,进行升序输出
	int i = 0;
	while (!HPEmpty(&hp)) {
		//printf("%d ", HPTop(&hp));
		a[i++] = HPTop(&hp);
		HPPop(&hp);
	}
	printf("Ascending sort: \n");
	for (size_t i = 0; i < sizeof(a) / sizeof(a[0]); i++) {
		printf("%d ", a[i]);
	}
	printf("\n");
	HPDestory(&hp);
}
int main() {
	TestHeap1();
	return 0;
}

输出结果如下: 

注:1、这种排序思路为实现排序需要额外开辟空间建堆,因为其调用了HPPush方法,空间复杂度为O(N);

2、实现打印升序与降序只需修改向上调整和向下调整方法的对比大小关系,即可实现大根堆的降序和小根堆的升序排序:

2. 基于原数组的堆排序(仅调AdjustUp)

1、回顾已实现的Adjust方法:

void AdjustUp(HPDataType* a, int child);

其参数为数组与新插元素的下标,直接将数组视为一个完全二叉树,并不需要依赖于HPPush方法,故并不额外开辟空间,空间复杂度为O(1);

现不调用HPPush方法,即对于已知数组不建堆,基于原数组仅调用向上调整算法进行堆排序

2、以大根堆为例,调用AdjustUp方法后,在原数组上已经实现了大根堆的顺序存储(但并未申请额外空间),考虑其输出升降序问题:

(1)若想要实现降序排序:

调用AdjustUp方法后,当前数组已满足首元素为最大元素,则输出并删除堆顶的最大元素后,接下来需在剩下的元素中建新大根堆并再删除堆顶最大元素,这与初始考虑的直接删除堆顶元素的HPPop方法思路一致,会导致节点关系的错位与混乱,如此循环往复建堆与调整,虽可以实现最终的排序效果,但显然非常麻烦;

(2)若想要实现升序排序:

调用AdjustUp方法后,当前数组已满足首元素为最大元素,将堆顶元素与最末结点元素交换,再将其位置确定在数组尾,即现确定了最大的元素且保持原堆的大根堆基本关系与结构不变,再进行向下调整即可。如此循环,每次都确定未排序元素中最大的结点,并将其依次从后向前定位在数组中,从而实现了升序排序;

#define _CRT_SECURE_NO_WARNINGS
#include"Heap.h"
// 测试不建堆的排序
void HeapSort(int* a, int n) {
	// 建堆
	for (int i = 1; i < n; i++) {
		AdjustUp(a, i);
	}
	// 打印大根堆的顺序存储序列
	printf("maxHeap array: \n");
	for (size_t i = 0; i < n; i++) {
		printf("%d ", a[i]);
	}
	printf("\n");
	// 交换首尾并删尾,再进行向下调整
	int end = n - 1;
	while (end>0) {
		Swap(&a[0], &a[end]);
		AdjustDown(a, end,0);
		end--;
	}
	// 打印升序排序结果
	printf("Ascending sort: \n");
	for (int i = 0; i < n; i++) {
		printf("%d ", a[i]);
	}
	printf("\n");
}
int main() {
	int a[] = { 4,2,8,1,5,6,9,7 };
	int sz = sizeof(a) / sizeof(a[0]);
	HeapSort(a,sz);
	return 0;
}

 运行程序,结果为:

3. 堆排序建堆与升降序的总结

对于实现堆排序:

1、若采用先建堆再排序的方法,则建大根堆实现降序,建小根堆实现升序;(通常不用)

(1)这种方式需要调用HPPush方法,HPPush方法内部自行调用AdjustUp方法,并使用HPTop方法获取堆顶元素并逐个pop以实现降序或升序输出;

(2)这种方式的空间复杂度为O(N);(较高)

2、若采用不建堆直接排序的方法,则建大根堆实现升序,小根堆实现降序;(常用)

(1)这种方式无需调用HPPush方法,直接调用AdjustUp方法,将原数组处理为小根堆或大根堆的顺序存储序列,交换首尾元素并确定尾元素位置后,对其余元素调用AdjustDown方法再确认倒数第二个元素的位置,循环进行直至形成降序或升序排列;

(2)这种方式的时间复杂度为O(N*log N);(较低)


http://www.kler.cn/a/544292.html

相关文章:

  • SpringCloud系列教程:微服务的未来(二十三)SpringAMQP快速入门、Work Queues、Fanout交换机
  • nexus部署及配置https访问
  • 【prompt实战】旅行攻略顾问
  • 【SpringBoot实现全局API限频】 最佳实践
  • 自己部署DeepSeek 助力 Vue 开发:打造丝滑的标签页(Tabs)
  • Spring:Spring实现AOP的通俗理解(有源码跟踪)
  • 【学习记录】AVL树及相关链表,线程池实现
  • 【matlab优化算法-16期】基于遗传算法的电热气及储能综合优化项目实践
  • HCIA项目实践--动态路由的相关知识
  • 六西格玛设计培训如何破解风电设备制造质量与成本困局
  • 如何使用deepseek等AI工具辅助前端工作的开发
  • 网络跨域问题深度解析与解决方案
  • 3. CSS中@scope
  • Haskell语言的软件工程
  • 从零开始学习人工智能
  • PostgreSQL 开发利器:Navicat 核心功能与资源攻略
  • Python-基于PyQt5,Pillow,pathilb,imageio,moviepy,sys的GIF(动图)制作工具(最终)
  • Python:座位分配
  • deepseek本地部署教程
  • 团结引擎高性能ECS架构(上)
  • 【deepseek-r1本地部署】
  • SpringAI ollama + deepseek-r1模型整合案例(含代码)
  • 期权帮|股指期货保证金制度解析!
  • 数据分析如何做EDA
  • Kafka因文件句柄数过多导致挂掉的排查与解决
  • React使用 useImperativeHandle 自定义暴露给父组件的实例方法(包括依赖)