排序算法:堆排序,golang实现

目录

前言

堆排序

代码示例

1. 算法包

2. 堆排序代码

3. 模拟程序

4. 运行程序

5. 从大到小排序

堆排序的思想

堆排序的实现逻辑

1. 构建最大堆

2. 排序

循环次数测试

假如 10 条数据进行排序

假如 20 条数据进行排序

假如 30 条数据进行排序

假设 5000 条数据,对比 冒泡、选择、插入、快速、归并

堆排序的适用场景

1. 大数据集排序

2. 外部排序

3. 优先级队列

4. 动态数据排序


前言

在实际场景中,选择合适的排序算法对于提高程序的效率和性能至关重要,本节课主要讲解"堆排序"的适用场景及代码实现。

堆排序

堆排序(Heap Sort)是一种基于比较的排序算法,它利用堆这种数据结构所设计。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。在堆排序算法中,我们通常采用最大堆(每个父节点的值都大于或等于其子节点的值)来进行排序。

代码示例

下面我们使用Go语言实现一个堆排序

1. 算法包

创建一个 pkg/algorithm.go

touch pkg/algorithm.go

如果看过上节课的快速排序,则已存在该文件,我们就不需要再创建了

2. 堆排序代码

打开 pkg/algorithm.go 文件,代码如下

从小到大 排序

package pkg// BubbleSort 冒泡排序
...// SelectionSort 选择排序
...// InsertionSort 插入排序
...// QuickSort 快速排序
...// partition 分区操作
...// HeapSort 堆排序
func HeapSort(arr []int) {n := len(arr)// 构建最大堆for i := n/2 - 1; i >= 0; i-- {heapify(arr, n, i)}// 一个个从堆顶取出元素for i := n - 1; i >= 0; i-- {// 移动当前根到末尾arr[i], arr[0] = arr[0], arr[i]// 调用 max heapify on the reduced heapheapify(arr, i, 0)}
}// heapify 将以 i 为根的子树调整为最大堆
func heapify(arr []int, n int, i int) {largest := i // 初始化最大为根l := 2*i + 1 // 左子节点r := 2*i + 2 // 右子节点// 如果左子节点大于根if l < n && arr[l] > arr[largest] {largest = l}// 如果右子节点大于当前的最大值if r < n && arr[r] > arr[largest] {largest = r}// 如果最大值不是根if largest != i {arr[i], arr[largest] = arr[largest], arr[i] // 交换// 递归地堆化受影响的子树heapify(arr, n, largest)}
}

3. 模拟程序

打开 main.go 文件,代码如下:

package mainimport ("demo/pkg""fmt"
)func main() {// 定义一个切片,这里我们模拟 10 个元素arr := []int{84, 353, 596, 848, 425, 849, 166, 521, 228, 573}fmt.Println("Original data:", arr) // 先打印原始数据pkg.HeapSort(arr)                  // 调用堆排序fmt.Println("New data:  ", arr)    // 后打印排序后的数据
}

4. 运行程序

go run main.go

能发现, Original data 后打印的数据,正是我们代码中定义的切片数据,顺序也是一致的。

New Data 后打印的数据,则是经过堆排序后的数据,是从小到大的。

5. 从大到小排序

如果需要 从大到小 排序也是可以的,在代码里,需要将两个 if 判断比较的 符号 进行修改。

修改 pkg/algorithm.go 文件:

package pkg// BubbleSort 冒泡排序
...// SelectionSort 选择排序
...// InsertionSort 插入排序
...// QuickSort 快速排序
...// partition 分区操作
...// HeapSort 堆排序
func HeapSort(arr []int) {n := len(arr)// 构建最大堆for i := n/2 - 1; i >= 0; i-- {heapify(arr, n, i)}// 一个个从堆顶取出元素for i := n - 1; i >= 0; i-- {// 移动当前根到末尾arr[i], arr[0] = arr[0], arr[i]// 调用 max heapify on the reduced heapheapify(arr, i, 0)}
}// heapify 将以 i 为根的子树调整为最大堆
func heapify(arr []int, n int, i int) {largest := i // 初始化最大为根l := 2*i + 1 // 左子节点r := 2*i + 2 // 右子节点// 如果左子节点小于根if l < n && arr[l] < arr[largest] {largest = l}// 如果右子节点小于当前的最大值if r < n && arr[r] < arr[largest] {largest = r}// 如果最大值不是根if largest != i {arr[i], arr[largest] = arr[largest], arr[i] // 交换// 递归地堆化受影响的子树heapify(arr, n, largest)}
}

只需要一丁点的代码即可

从 package pkg 算第一行,上面示例中在第四十四行代码,第四十九行代码,我们将 ">" 改成了 "<" ,这样就变成了 从大到小排序了

堆排序的思想

  • 利用堆的性质:堆排序利用堆的性质,通过不断调整堆来使得每次都能从堆顶取出当前序列的最大(或最小)元素,从而达到排序的目的
  • 原地排序:堆排序是一种原地排序算法,它只需要用到 O(1) 的额外空间来进行排序(除了输入的数组外,不需要使用其他数据结构)
  • 不稳定性:堆排序是一种不稳定的排序算法,因为在调整堆的过程中,可能会改变相同元素的相对顺序
  • 时间复杂度:堆排序的时间复杂度是 O(n log n),这主要来自于构建最大堆和每次调整堆的时间复杂度

堆排序的实现逻辑

堆排序主要分为两个步骤:

1. 构建最大堆

  • 将待排序的序列构造成一个最大堆,此时,整个序列的最大值就是堆顶的根节点
  • 构建最大堆的过程是从最后一个非叶子节点开始(即 n/2-1 位置,因为数组是从 0 开始索引的),对每个非叶子节点调用 heapify 函数,使其和其子树满足最大堆的性质

2. 排序

  • 将堆顶元素(最大值)与堆数组的末尾元素进行交换,此时末尾就是最大值
  • 由于堆的大小减少 1,我们再次将堆顶元素调整为最大值,以满足最大堆的性质
  • 重复这个过程,直到堆的大小为 1,算法结束

循环次数测试

参照上面示例进行测试(因考虑到每次手动输入 10 条、20 条、30 条数据太繁琐,所以我写了一个函数,帮助我自动生成 0到1000 的随机整数)

假如 10 条数据进行排序

总计循环了 32 

假如 20 条数据进行排序

总计循环了 79 

假如 30 条数据进行排序

总计循环了 136 

假设 5000 条数据,对比 冒泡、选择、插入、快速、归并

  • 冒泡排序:循环次数 12,502,499
  • 选择排序:循环次数 12,502,499
  • 插入排序:循环次数 6,323,958
  • 快速排序:循环次数 74,236 次
  • 堆排序:循环次数 59,589 次
  • 归并排序:循环次数 60,288 次

堆排序的适用场景

堆排序特别适用于以下场景

1. 大数据集排序

由于堆排序的时间复杂度是 O(n log n),在处理大数据集时效率较高

2. 外部排序

当数据太大,不能全部加载到内存时,可以使用堆排序进行外部排序,因为它只需要读取一次输入数据,然后逐步输出排序结果

3. 优先级队列

堆经常被用作优先级队列的实现方式,堆排序可以看作是从无序的优先队列中重建有序的优先队列的过程

4. 动态数据排序

当数据集合动态变化(如插入、删除操作频繁),堆排序的堆结构可以高效地维护数据的排序状态

总的来说,堆排序因其良好的最坏情况时间复杂度,以及对动态数据排序的友好性,在多种场景下都是非常有用的排序算法

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 串口控制 校验

1. 串口控制 using System; using System.IO.Ports; using System.Windows.Forms;namespace 串口控制 {public partial class Form1 : Form{//device1const byte DeviceOpen1 0x01;const byte DeviceClose1 0x81;//device2const byte DeviceOpen2 0x02;const byte DeviceCl…

git 、shell脚本

git 文件版本控制 安装git yum -y install git 创建仓库 将文件提交到暂存 git add . #将暂存区域的文件提交仓库 git commit -m "说明" #推送到远程仓库 git push #获取远程仓库的更新 git pull #克隆远程仓库 git clone #分支&#xff0c;提高代码的灵活性 #检查分…

【C++进阶学习】第十一弹——C++11(上)——右值引用和移动语义

前言&#xff1a; 前面我们已经将C的重点语法讲的大差不差了&#xff0c;但是在C11版本之后&#xff0c;又出来了很多新的语法&#xff0c;其中有一些作用还是非常大的&#xff0c;今天我们就先来学习其中一个很重要的点——右值引用以及它所扩展的移动定义 目录 一、左值引用和…

step:菜单栏静态加载和动态加载

文章目录 文章介绍静态加载动态加载补充材料 文章介绍 对比静态加载和动态加载。 主界面main.qml之前使用的是动态加载&#xff0c;动态加载导致的问题&#xff1a;菜单栏选择界面切换时&#xff0c;之前的界面内容被清空。 修改方法&#xff1a;将动态加载改为静态加载 左边是…

九大原则,轻松构建个人高效SOP

1、原则一、工作汇报SOP SCQA模型(升职加薪的关键!&#xff09; 清晰定义问题和提出解决方案 类别 关键词 解读 S - Situation 情景 陈述项目背景&#xff0c;目标&#xff0c;愿景 C - Complication 冲突 讲卡点&#xff0c;讲冲突 Q - Question 疑问-问题 这些冲…

MyBatis基础配置

一、M y B a t i s 配 置 文 件 MyBatis配置文件的功能&#xff1a;构建SqlSessionFactory的依据 MyBatis配置文件的意义&#xff1a;MyBatis最为核心的内容&#xff0c;对MyBatis的使用影响很大。 MyBatis配置文件注意事项&#xff1a;配置文件的层次顺序不能颠倒&#xff0c;…

镜像制作和管理

文章目录 一、Docker镜像说明Docker镜像中没有内核为什么没有内核容器中的程序后台运行会导致此容器启动后立即退出镜像的生命周期和制作方式 二、手动构建镜像基于容器手动制作镜像步骤实际操作基于 busybox 制作httpd镜像制作tomcat镜像基于ubuntu的基础镜像手动安装nginx镜像…

Python基础教程(三)类和对象、异常处理和模块

8.类与对象 8.1 面向对象 面向对象的三大基本特征: 封装、继承、多态。 在面向对象编程中&#xff0c;封装&#xff08;Encapsulation&#xff09;是一种将数据和操作&#xff08;方法&#xff09;组合在一起的机制。通过封装&#xff0c;我们可以隐藏数据的具体实现细节&am…

RuoYi-Vue-Plus (多数据源注解使用、【手动、拦截器】切换数据源)

接上文多数据源配置&#xff1a; RuoYi-Vue-Plus (多数据源配置)-CSDN博客 一、功能演示 代码生成菜单页面&#xff0c; 展示数据源切换 查询主库 查询从库 二、前端传参切换数据源 页面路径&#xff1a; src/views/tool/gen/index.vue 搜索框如下&#xff1a;下面4发送请求时…

技术分享| 前端性能优化——虚拟滚动(Virtual Scroll)

前端遇到大量数据&#xff08;尤其是大数据表&#xff09;的DOM 渲染时&#xff0c;通常会卡顿&#xff0c;需要考虑优化性能问题&#xff0c;这里针对DOM 渲染引出“虚拟滚动”方案&#xff0c; 详细请在以下各文章中详细了解&#xff1a; vue插件 vue-virtual-scroll-list解决…

干货满满,从零到一:编程小白如何在大学成为编程大神?

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

ClickHouse 24.6 版本发布说明

本文字数&#xff1a;14127&#xff1b;估计阅读时间&#xff1a;36 分钟 作者&#xff1a;ClickHouse team 本文在公众号【ClickHouseInc】首发 又到了发布新版本的时间&#xff01; 发布概要 本次ClickHouse 24.6 版本包含了23个新功能&#x1f381;、24项性能优化&#x1f6…

嵌入式人工智能(39-基于树莓派4B的震动传感器和霍尔传感器)

这两个传感器实验比较简单&#xff0c;也都属于力传感器&#xff0c;就放一起做了。 1、震动传感器 震动传感器是一种用于检测和测量物体震动、振动和冲击的设备。它通常由一个敏感元件和一个信号处理单元组成。敏感元件可以是压电材料、光电材料、加速度传感器等。当物体发生…

【Git】git 从入门到实战系列(一)—— Git 的诞生,Linus 如何在 14 天内编写出 Git?

<> 博客简介&#xff1a;Linux、rtos系统&#xff0c;arm、stm32等芯片&#xff0c;嵌入式高级工程师、面试官、架构师&#xff0c;日常技术干货、个人总结、职场经验分享   <> 公众号&#xff1a;嵌入式技术部落   <> 系列专栏&#xff1a;C/C、Linux、rt…

golang JSON序列化

JSON JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集。 json历史 [外链图片转存失败,源站可能有防盗链机…

看不见的硝烟:中国网络安全三十年沉浮史

2022 年 5 月 16 日&#xff0c;俄罗斯黑客组织 KillNet 向包括美国、英国、德国在内 10 个国家的政府正式 “宣战”。 2022 年 4 月 28 日&#xff0c;一则消息刷屏&#xff0c;北京健康宝在使用高峰期间&#xff0c;遭受到境外网络攻击。北京健康宝保障团队进行了及时有效应…

4G/5G无线视频采集设备如何通过国标28181接入到视频监控接入平台(视频统一接入平台)

目录 一、国标GB/T 28181介绍 1、国标GB/T28181 2、内容和特点 二、4G/5G无线视频采集设备 1、定义 2、主要功能&#xff1a; 3、技术特点 4、应用场景 二、接入准备工作 1、确定网络环境 &#xff08;1&#xff09;公网接入 &#xff08;2&#xff09;专网传输 2、…

一款功能强大的免费开源卸载工具

BCUninstaller&#xff0c;也称为Bulk Crap Uninstaller&#xff08;简称BCU&#xff09;&#xff0c;是一款免费且开源的Windows平台专用程序卸载工具。它的主要功能是帮助用户高效地批量卸载不需要的应用程序和组件&#xff0c;从而优化系统性能。 BCUninstaller功能特点 批…

这本vue3编译原理开源电子书,初中级前端竟然都能看懂

前言 众所周知vue提供了很多黑魔法&#xff0c;比如单文件组件(SFC)、指令、宏函数、css scoped等。这些都是vue提供的开箱即用的功能&#xff0c;大家平时用这些黑魔法的时候有没有疑惑过一些疑问呢。 我们每天写的vue代码一般都是写在*.vue文件中&#xff0c;但是浏览器却只…

【大厂笔试】翻转、平衡、对称二叉树,最大深度、判断两棵树是否相等、另一棵树的子树

检查两棵树是否相同 100. 相同的树 - 力扣&#xff08;LeetCode&#xff09; 思路解透 两个根节点一个为空一个不为空的话&#xff0c;这两棵树就一定不一样了若两个跟节点都为空&#xff0c;则这两棵树一样当两个节点都不为空时&#xff1a; 若两个根节点的值不相同&#xff…