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

【面试题】数据结构:堆排序的排序思想?

堆排序的排序思想?

请添加图片描述
堆排序是一种高效的排序算法,其基本思想是利用堆这种数据结构来实现排序。堆是一种特殊的完全二叉树,通常用数组来表示。堆排序的基本步骤如下:

1. 构建初始堆:

  • 将待排序的数组转换成一个最大堆(或最小堆)。在最大堆中,父节点的值总是大于或等于其子节点的值。转换过程从最后一个非叶子节点开始,向上调整堆,确保堆的性质。

2. 堆排序过程:

  • 将堆顶元素(最大值或最小值)与最后一个元素交换,然后将剩余的元素重新调整为堆。
  • 重复上述过程,每次将堆顶元素与当前未排序部分的最后一个元素交换,并重新调整堆,直到整个数组被排序。

3. 调整堆:

  • 每次交换后,需要调整堆以保持堆的性质。调整过程从交换后的堆顶元素开始,向下调整,确保每个节点都满足堆的性质。

4. 循环结束:

  • 当所有元素都通过堆调整并交换到数组的前部时,排序完成。

具体步骤:

  1. 假设数组长度为n,初始时数组为A[1…n]。
  2. 将数组从后向前转换为最大堆:
  • 从最后一个非叶子节点开始(即A[n/2]),向下调整堆。
  • 每个节点向下调整时,比较其值与其子节点的值,如果当前节点的值小于其子节点的值,则与较大的子节点交换。
  • 重复上述过程,直到堆顶元素满足最大堆的性质。
  1. 将堆顶元素(最大值)与数组的最后一个元素交换,然后重新调整堆。
  2. 重复上述过程,直到堆的大小减少到1。

时间复杂度:

  • 堆排序的时间复杂度为O(n log n),其中n是数组的长度。

空间复杂度:

  • 堆排序是原地排序算法,不需要额外的存储空间,因此空间复杂度为O(1)。

稳定性:

  • 堆排序不是稳定的排序算法,因为相同的元素在排序过程中可能会交换位置。

代码:

// 向下调整算法,使以 parent 为根节点的堆满足大根堆性质
void AdjustDown(int* a, int parent, int n)
{assert(a);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 = parent * 2 + 1;}else{break; // 父节点已经大于等于子节点,退出循环}}
}// 堆排序算法
void HeapSort(int* a, int n)
{// 升序排序建大根堆,降序排序建小根堆for (int i = (n - 1) / 2; i >= 0; i--) // 从最后一个非叶子节点开始向下调整{AdjustDown(a, i, n); // 向下调整以 i 为根节点的大根堆}int end = n - 1;while (end > 0){Swap(&a[0], &a[end]); // 将堆顶元素(即最大值)与堆末尾元素交换AdjustDown(a, 0, end); // 对新的堆顶进行向下调整,使其满足大根堆性质--end; // 堆大小减 1,排除已排序好的最大值}
}

使用 priority_queue 实现

逆序:

void heapSort(vector<int>& nums) {priority_queue<int> maxHeap;// 将数组元素插入最大堆中for (int num : nums) {maxHeap.push(num);}// 依次取出堆顶元素放入结果数组中(逆序)for (int i = nums.size() - 1; i >= 0; --i) {nums[i] = maxHeap.top();maxHeap.pop();}
}

顺序:

void heapSort(vector<int>& nums) {priority_queue<int, vector<int>, greater<int>> minHeap;// 将数组元素插入最小堆中for (int num : nums) {minHeap.push(num);}// 依次取出堆顶元素放入结果数组中(顺序)for (int i = 0; i < nums.size(); ++i) {nums[i] = minHeap.top();minHeap.pop();}
}

相关文章:

  • 辅助类BigDecima/BigInteger
  • 【Windows】操作系统之任务管理器(第一篇)
  • 车载音视频App框架设计
  • 前端pc和小程序接入快递100(跳转方式和api方式)====实时查询接口
  • Self-supervised Learning for Pre-Training 3D Point Clouds: A Survey
  • 如何免费用java c#实现手机在网状态查询
  • 【Apache POI】Java解析Excel文件并处理合并单元格-粘贴即用
  • Java 在PDF中替换文字(详解)
  • Google资深工程师深度讲解Go语言-课程笔记
  • 一个简单的springboot应用搭建过程
  • 第2部分:物联网模式在行动
  • Vue进阶之Vue无代码可视化项目(七)
  • 口袋奇兵游戏攻略:云手机辅助战锤入侵策略指南!
  • 防御综合实验作业2
  • Web开发:<br>标签的作用
  • Angular 4.x 动态创建组件
  • CSS居中完全指南——构建CSS居中决策树
  • Git同步原始仓库到Fork仓库中
  • Laravel 中的一个后期静态绑定
  • Mac转Windows的拯救指南
  • mockjs让前端开发独立于后端
  • mysql innodb 索引使用指南
  • vue脚手架vue-cli
  • 编写符合Python风格的对象
  • 创建一个Struts2项目maven 方式
  • 基于Dubbo+ZooKeeper的分布式服务的实现
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 前端之React实战:创建跨平台的项目架构
  • 使用权重正则化较少模型过拟合
  • 手机端车牌号码键盘的vue组件
  • 微服务入门【系列视频课程】
  • 微信小程序实战练习(仿五洲到家微信版)
  • 小程序开发中的那些坑
  • 用 vue 组件自定义 v-model, 实现一个 Tab 组件。
  • 阿里云ACE认证之理解CDN技术
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • #QT项目实战(天气预报)
  • #我与Java虚拟机的故事#连载07:我放弃了对JVM的进一步学习
  • #知识分享#笔记#学习方法
  • (C#)一个最简单的链表类
  • (C++)八皇后问题
  • (leetcode学习)236. 二叉树的最近公共祖先
  • (动手学习深度学习)第13章 计算机视觉---图像增广与微调
  • (二)WCF的Binding模型
  • (翻译)Entity Framework技巧系列之七 - Tip 26 – 28
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (原)记一次CentOS7 磁盘空间大小异常的解决过程
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • (自适应手机端)行业协会机构网站模板
  • ***微信公众号支付+微信H5支付+微信扫码支付+小程序支付+APP微信支付解决方案总结...
  • *2 echo、printf、mkdir命令的应用
  • .NET 4.0中的泛型协变和反变
  • .NET Core Web APi类库如何内嵌运行?
  • .net redis定时_一场由fork引发的超时,让我们重新探讨了Redis的抖动问题