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

数据结构--堆排序(超详细!)

一、前言

堆排序与Top K问题是堆的两大应用,在我们日常也有很广泛的用处

我们已经上面已经说过了堆,这次来说堆的其中一个应用---堆排序。
 

二、堆排序

堆排序优势在哪里?有什么恐怖之处吗?

重点:拿一个举例:我们上一篇博客在代码运用过程中,我们的HeapPop函数每次删除堆顶元素之后进行向下调整之后,都能找到次大或者次小的值。

int main()
{HP php;InitHeap(&php);int a[] = { 4,7,2,3,9,5,1 };for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)//建堆{PushHeap(&php, a[i]);}while (!HeapEmpty(&php))//进入循环,直至堆为空{printf("%d ", HeapTop(&php));//打印堆顶元素HeapPop(&php);//删除堆顶元素}DestroyHeap(&php);return 0;
}

  这样就把我们所想要的排序好了,我们每取出一个次大或者次小的值,时间复杂度都是O(log N),当有1000个数据时,各个元素比较查找那就需要进行1000次,而我们这种做法10次就够了。效率很高。分析上面代码,还是有点缺陷的,在我们建堆的时候,我们是从源头数组里面取数据,再新开辟空间建堆,源头数组并没有变化,这便造成了空间的浪费。所以,有没有一种高效又节省空间的方法呢----堆排序。

  我们要想节省空间,最好需要做到不另开辟空间,我们已经定义了一个数组,并且想将数组里面的元素进行排序,那为什么我们不在原数组上找路子呢?

1、建堆

 开辟空间的建堆方式,先将源头数组里面的元素放入我们开辟的结构体中的数组里,再进行向上调整建堆。那为何我们不直接将源头数组元素直接进行向上调整呢?

我们可以直接将原数组传给AdjustUp函数,再先将数组中第一个元素进行向上调整,接着再第二个元素,这样依次进行向上调整。

int a[] = { 4,7,2,3,9,5,1,6,8};int n = sizeof(a) / sizeof(a[0]);int i = 0;for (i = 1; i < n; i++){AdjustUp(&a, i);}

我们打印一下看看结果:

因为我们上述向上调整函数最终调整为小堆,这里就拿小堆来做参考:

监视查看数组的变化:

  可以看到,数组中元素已经是小堆。这样既满足了我们的建堆要求,也降低了空间的消耗

2、排序

  那么,话说回来,堆是随便建的吗?直接建一个大堆或者小堆都可以满足我们要求吗?有没有什么要求呢?

  我们在开头举国一个例子:我们的HeapPop函数每次删除堆顶元素之后进行向下调整之后,都能找到次大或者次小的值。

  HeapPop函数的逻辑是,将堆顶元素A与堆尾元素B互换,然后A删除,将B向下调整建堆。

  如果刚开始建的是小堆,我们交换堆顶元素A和队尾元素B后不着急删除A,既然是小堆,那么A肯定是所有元素中的最小值,交换后在队尾。我们再进行向下调整后,重建建小堆(这里重新建堆时不要将A也加入建堆的操作中,因为我们没有删除A),这时候的堆顶元素就是次小的值。我们将堆顶元素再与倒数第二个元素进行交换,这样次小的值就在倒数第二个位置了,再进行向下调整。这两个操作多次进行,直到剩下最后一个元素。我们每次都是找次小的值将它放在数组中最后一个,这样下来,我们最终就会得到排序好的元素,为降序

  如果是大堆呢?每次向下调整都会找到次大的值,堆顶元素与堆尾元素交换后,次大的值依次从后向前排列,最后我们便得到排序好的元素,为升序。

 

所以我们可以总结出,升序建大堆,降序建小堆。

代码如下(以降序举例):

int main()
{int a[] = { 4,7,2,3,9,5,1,6,8};int n = sizeof(a) / sizeof(a[0]);int i = 0;//在数组里建堆,减少了空间消耗//升序建大堆,降序建小堆 for (i = 1; i < n; i++){AdjustUp(&a, i);}//查看建堆后的元素排列for (i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");//重新定义一个变量,将元素大小赋值给它.不改变nint end = n - 1;while(end > 0){Swap_HP(&a[0],&a[end]);//交换堆顶元素与堆尾元素AdjustDown(&a,end, 0);//向下调整找到此小值end--;//在下一次交换中,让新的堆顶元素与新的堆尾元素交换。}for (i = 0; i < n; i++){	printf("%d ", a[i]);}return 0;
}

这一趟下来时间复杂度只有N*logN,比最开始学的冒泡排序快了不少。数据越多,就越能体现出堆排序的优越性!

以上就是堆排序要说的所有内容。

相关文章:

  • Postman-接口测试教程
  • bash 5.2中文修订5
  • visual studio2022专业版安装步骤
  • 第一节课,用户管理--后端初始化,项目调通。二次翻工2
  • Flink CEP实现10秒内连续登录失败用户分析
  • 如何获得《幻兽帕鲁》隐藏帕鲁唤夜兽?13000个配种配方查询 幻兽帕鲁Steam好评率还在涨 Mac苹果电脑玩幻兽帕鲁 Crossover玩Windows游戏
  • 腾讯mini项目总结-指标监控服务重构
  • 【EMI静噪滤波器(EMC降噪对策)概要】 BLM□□H Series UHF频带静噪效果
  • 【python】符号运算
  • 如何在Java中添加元素到集合?
  • 12.从项目经理的生存哲学到适配器模式(Adapter Pattern)
  • python-自动化篇-运维-监控-简单实例-道出如何使⽤Python进⾏系统监控?
  • 【React】前端项目引入阿里图标
  • 8-小程序数据promise化、共享、分包、自定义tabbar
  • 华为机考入门python3--(3)牛客3-明明的随机数
  • 【5+】跨webview多页面 触发事件(二)
  • Centos6.8 使用rpm安装mysql5.7
  • conda常用的命令
  • ES10 特性的完整指南
  • JavaScript学习总结——原型
  • Laravel 实践之路: 数据库迁移与数据填充
  • Mysql数据库的条件查询语句
  • unity如何实现一个固定宽度的orthagraphic相机
  • 半理解系列--Promise的进化史
  • 从setTimeout-setInterval看JS线程
  • 记录:CentOS7.2配置LNMP环境记录
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 如何选择开源的机器学习框架?
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 线上 python http server profile 实践
  • 新版博客前端前瞻
  • 用 vue 组件自定义 v-model, 实现一个 Tab 组件。
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • ## 临床数据 两两比较 加显著性boxplot加显著性
  • #define与typedef区别
  • (27)4.8 习题课
  • (C语言)逆序输出字符串
  • (LeetCode 49)Anagrams
  • (二)springcloud实战之config配置中心
  • (翻译)terry crowley: 写给程序员
  • (转)Java socket中关闭IO流后,发生什么事?(以关闭输出流为例) .
  • ***通过什么方式***网吧
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .dat文件写入byte类型数组_用Python从Abaqus导出txt、dat数据
  • .NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式
  • .NET MVC 验证码
  • .NET 表达式计算:Expression Evaluator
  • .Net高阶异常处理第二篇~~ dump进阶之MiniDumpWriter
  • .NET构架之我见
  • .NET国产化改造探索(一)、VMware安装银河麒麟
  • .net经典笔试题
  • .NET中的Event与Delegates,从Publisher到Subscriber的衔接!
  • @SentinelResource详解