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

【数据结构】第十八弹---C语言实现堆排序

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】

目录

1、堆排序

1.1、基本思想

1.2、初步代码实现

1.3、代码优化

1.4、代码测试

总结


1、堆排序

在博主数据结构第十二弹---堆的应用有详细讲解堆排序喔~~~

数据结构第十二弹---堆的应用https://blog.csdn.net/2201_75584283/article/details/135348207icon-default.png?t=N7T8https://blog.csdn.net/2201_75584283/article/details/135348207

1.1、基本思想

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆

为什么升序用到的是大堆呢?

因为:大堆的堆顶是最大的数,可以将其放在数组尾,然后再通过向下调整算法找到次大的数。而小堆的堆顶是最小的数,需要放在第一个位置,如果用原来的堆找不到次小的数,而重新建堆则会更加繁琐。

降序用小堆同理。

动图如下:

1.2、初步代码实现

堆排序的实现可以分为两部分:构建最大堆(或最小堆)和执行排序过程。

注意:此处我们实现的是大堆!!!

第一步:建堆

我们此处是对数组内部的数进行排序,那么怎样才能创建成大堆呢?

这里我们可以使用向上调整算法,从第二个数开始遍历数组,如果不满足大堆则交换父子元素。

for (int i = 1; i < n; i++)
{AjustUp(a, i);
}

大堆向上调整:

  1. 将被调整的数值与其父节点比较,若是大于父节点,则与父节点交换。
  2. 若子节点下标为child,父节点下标为(child - 1) / 2。
  3. 当子节点小于父节点时,或者当子节点已经为堆顶时,停止比较。

代码实现:

void AdjustUp(int* a, int child)
{int parent = 0;while (child > 0){parent = (child - 1) / 2;if (a[child] > a[parent]){Swap(&a[child], &a[parent]);child = parent;}else{break;}}
}

小堆向上调整:

 与向下调整大堆思想的唯一区别就是:将被调整的数值与父节点比较,若是小于父节点,则与父节点交换,将小根堆比较条件改为小于即可

if (a[child] < a[parent])//孩子小于父亲则交换
{...
}

每次向上调整算法的时间复杂度为O(log N)。 

​ 所以使用向上调整建好堆的时间复杂度为(N*log N)

第二步:执行排序操作

进行了向上调整之后,此时的数组就是一个大堆了,要怎样才能达到升序呢?

  1. 使用大根堆选出最大的数,放在数组的最后一个位置,依次选出进行排序。
  2. 将堆顶和最后一个数交换。
  3. 然后将新堆顶向下调整,形成堆。
  4. 向下调整时,注意交换后的最后位置不在新堆里,所以要下标要减一。
  5. 没有对堆结构造成破坏,不用对每个数都调整。
//2.向下调整 O(N*logN)
int end = n - 1;
while (end > 0) //从最后一个位置开始交换并调整
{Swap(&arr[0], &arr[end]);AdjustDown(arr, end, 0);//此处为大根堆向下调整方式end--;
}

 大堆向下调整:

  1. 将被调整的数值与其最大的子节点比较,若是小于最大的子节点,则与该子节点交换。
  2. 若父节点下标为parent,左子节点下标为 parent * 2 + 1,右子节点的下标为 parent * 2 + 2。
  3. 获取最大的子节点时,可以先将左子节点作为最大节点,再与右子节点比较,若是大于右子节点,则将左子节点下标加1得到右子节点下标。
  4. 再循环体内比较左右子节点之前,要先判断右子节点存在,防止堆最后一个节点是左子节点造成越界访问。
  5. 当子节点小于父节点时,或者当子节点超过堆的范围时,停止比较。

//向下调整算法 大堆
void AdjustDown(int* a, int size, int parent)
{//1.假设左孩子为小的数据int child = parent * 2 + 1;while (child < size){//2.如果左孩子>右孩子 则将右孩子赋值//有可能只有左孩子 所以加条件//以下未有左右孩子且左孩子>右孩子情况,则将child++if (child + 1 < size && a[child] < a[child + 1]){child++;}//3.将孩子与父亲进行比较 如果孩子小则交换//然后将父亲和孩子移动到下一个位置if (a[child] > a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}

小堆向下调整:

  1. 将被调整的数值与其最小的子节点比较,若是大于最小的子节点,则与该子节点交换。
  2. 将小根堆向下调整时左右子节点的比较条件和父节点与子节点的比较改为小于即可。
if (child + 1 < size && a[child] > a[child + 1])
{...	
}if (a[child] < a[parent])
{...
}

堆排序的代码如下:

void HeapSort(int arr[], int n)
{assert(arr);//1.建堆 向上调整 O(N*logN)for (int i = 1; i < size; i++){AdjustUp(arr, i);}//2.向下调整 O(N*logN)int end = n - 1;while (end > 0){Swap(&arr[0], &arr[end]);AdjustDown(arr, end, 0);end--;}
}

1.3、代码优化

在建堆的时候,我们既可以使用向上调整算法建堆,也可以使用向下调整算法建堆,在堆的应用那一弹我们计算了向下调整算法建堆的时间复杂度,对整个数组进行向下调整的时间复杂度是O(N),因此我们在堆排序的时候也可以统一使用向下调整算法!!!

向下调整:

  1. 向下调整是从后往前调整,先将后面构成堆,再调整上面的节点。
  2. 以叶子节点作为根进行向下调整是完全没有必要的,叶子节点没有子节点,所以对最后一个叶子节点的父节点开始向下调整。
  3. 最后一个节点下标是n-1,它的父节点为 (n-1-1) / 2。

//1.向下调整建堆 O(N)
for (int i = (n - 1 - 1) / 2; i >= 0; i--)//从n-2 到 0 进行调整
{AdjustDown(arr, n, i);
}

 

堆排序代码如下:

void HeapSort(int arr[], int n)
{assert(arr);//1.向下调整建堆 O(N)for (int i = (n - 1 - 1) / 2; i >= 0; i--)//从n-2 到 0 进行调整{AdjustDown(arr, n, i);}//2.向下调整 O(N*logN)int end = n - 1;while (end > 0){Swap(&arr[0], &arr[end]);AdjustDown(arr, end, 0);end--;}
}

1.4、代码测试

测试代码:

//测试堆排序
int main()
{int a[] = { 9,8,7,6,5,4,3,2,1,0 };//给一组数据int sz = sizeof(a) / sizeof(a[0]);//计算数组元素个数printf("排序前:\n");ArrayPrint(a, sz);HeapSort(a, sz);printf("排序后:\n");ArrayPrint(a, sz);return 0;
}

测试结果:

 

堆排序的特性总结:

1. 堆排序使用堆来选数,效率就高了很多。
2. 时间复杂度:O(N*logN)。
3. 空间复杂度:O(1)。
4. 稳定性:不稳定。

5. 复杂性:复杂。

总结

本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!

http://www.lryc.cn/news/377363.html

相关文章:

  • [面试题]Kafka
  • centos7 离线安装zip和unzip
  • Linux下lsof命令使用
  • 基于ChatGPT的大型语言模型试用心得
  • Python 列表添加多个值(四种方法)
  • VMware RedHat虚拟机磁盘扩容(添加磁盘和扩展磁盘)
  • 最近,GPT-4o横空出世。对GPT-4o这一人工智能技术进行评价,包括版本间的对比分析、GPT-4o的技术能力以及个人整体感受等
  • C#面:C#支持多重继承么?
  • 细说MCU修改回调函数调用模式的方法
  • Java共享台球室无人系统支持微信小程序+微信公众号
  • 如何开发一个海外仓系统?难度在哪,怎么选择高性价解决方案
  • 计算机组成原理(Wrong Question)
  • ACL2024 | AI的时空穿越记:大型语言模型共时推理的奇幻之旅!
  • 从xxl-job源码中学习Netty的使用
  • 人工智能发展历程了解和Tensorflow基础开发环境构建
  • makefile追加warning日志
  • 不要直接使用unidefined 而使用void 0
  • 注解详解系列 - @Scope:Bean作用域管理
  • 数学建模基础:数学建模概述
  • 人工智能大模型之开源大语言模型汇总(国内外开源项目模型汇总)
  • 数据结构之B树
  • 双色球预测算法(Java),——森林机器学习、时间序列
  • 【计算机网络篇】数据链路层(11)在数据链路层扩展以太网
  • Ubuntu20.04 使用scrapy-splash爬取动态网页
  • Function:控制继电器上下电,上电后adb登录,copy配置文件
  • 香港电讯高可用网络助力企业变革金融计算
  • LDR6020一拖二快充线:多设备充电新选择
  • 电脑ffmpeg.dll丢失原因解析,找不到ffmpeg.dll的5种解决方法
  • 手机网站制作软件是哪些
  • 【Kubernetes项目部署】k8s集群+高可用、负载均衡+防火墙