热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Go语言实现堆排序的详细教程

本文主要介绍了Go语言实现堆排序的详细教程,包括大根堆的定义和完全二叉树的概念。通过图解和算法描述,详细介绍了堆排序的实现过程。堆排序是一种效率很高的排序算法,时间复杂度为O(nlgn)。阅读本文大约需要15分钟。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Go(08)实现堆排序相关的知识,希望对你有一定的参考价值。



当前浏览器不支持播放音乐或语音,请在微信或其他浏览器中播放 Go(08)实现堆排序

阅读本文大约15分钟

本文难度:

前文介绍了golang实现基本的四中排序,本文带领大家实现堆排序,堆排序是效率很高的算法,通过取出大根堆堆顶元素从而实现排序的算法。
该算法以出色的效率著称,时间复杂度为O (nlgn)


堆排序描述


什么是大根堆

1 大根堆是一颗完全二叉树
2 该完全二叉树,根节点一定大于等于其左右子节点,并且大于等于其子树所有节点。


完全二叉树

完全二叉树是相对于满二叉树来讲的,对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。


满二叉树:

一棵深度为 k,且有 2k - 1 个节点称之为满二叉树,即每一层上的节点数都是最大节点数。

也就是说完全二叉树可以这么理解,在一棵二叉树中,除最后一层外,若其余层都是满的,并且最后一层或者是满的,或者是在右边缺少连续若干节点,则此二叉树为完全二叉树(Complete Binary Tree)

下图就是一个完全二叉树
Go(08)实现堆排序


算法图解

假设有序列
下标 | 0 | 1 | 2 | 3 | 4 | 5 |
数值 | 6 | 1 | 0 | 5 | 2 | 9 |
将该序列构造成上图的二叉树,首先从最后一个非叶子结点(也就是下标2的节点元素0)开始,将其所在子树调节为大根堆
Go(08)实现堆排序

由于9比0大,所以二者交换位置,这样下标2所在子树就变为大根堆了。接下来从右往左,从下往上依次处理所有非叶子节点所在子树,使其依次形成大根堆。下面处理下标为1的子树。
Go(08)实现堆排序

下标1的两个子节点中5比2大,选择最大的子节点5和1比较,由于5比1大,所以二者交换位置,这样下标1所在子树就变为大根堆了。接下来处理下标0所在子树,使其成为大根堆。

Go(08)实现堆排序

同样选择下标0最大的子节点9和下标0的元素6交换位置,这样最大元素9放在下标0的位置,由于6和9交换位置,还要考虑元素6所在的下标为1的子树是否因为交换导致失去大根堆特性,如果6比其子节点小,则继续将6下移,直到找到合理位置。此时6比其子节点0大,所以不需要移动了。

下标0就是根节点,到此为止一颗大根堆构造完成。
接下来将根节点9和最后一个元素交换,n为最后一个元素。这样调节前n-1节点,是这些节点构成大根堆。
具体操作如下图
交换最后一个元素和根节点
Go(08)实现堆排序

将最后一个元素排出不再比较,比较前n-1个元素,重新构造大根堆。
Go(08)实现堆排序

以此类推,
将n-1个节点调整为大根堆
Go(08)实现堆排序

前n-1调节为大根堆后,将根元素和第n-1元素交换

将第n-1个元素取出。

这样最后两个元素分别为6,9,是从小到大拍好的顺序,继续使前n-2节点形成大根堆。直到n=1


算法实现

首先定义一个HeapSort类,然后设计一个成员函数adjustHeap,该函数主要实现将index所在位置的子树构造成大根堆。
adjustHeap的三个参数分别为一段连续的数据序列,index表示子树根所在位置,length表示要排序的长度。










1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

type HeapSort struct {
}
//调整index为根的子树,此时index的左右子树都是大根树
//比较index和其左右节点,将index根节点设置为最大的元素
//可能会引起子树失效,所以会循环处理修改的子树
func (hs *HeapSort) adjustHeap(array []int, index, length int) {
//index 的左右子节点
leftchild := index*2 + 1
rightchild := leftchild + 1
maxchild := leftchild
for {
//如果左节点比长度大,说明该节点为子节点
if leftchild > length-1 {
break
}
//右节点存在,且比左节点大
if rightchild <= length-1 && array[rightchild] > array[maxchild] {
maxchild = rightchild
}
//index 元素比最大子节点大,则不需要交换,退出
if array[index] > array[maxchild] {
break
}
//比较自己元素和最大节点的元素,做交换
hs.swap(array, index, maxchild)
index = maxchild
leftchild = index*2 + 1
rightchild = leftchild + 1
maxchild = leftchild
}
}



接下来实现一个小函数,用来交换两个位置的元素










1
2
3
4
5
6
7
8
9
10

func (hs *HeapSort) swap(array []int, i, j int) {
if i >= len(array) || j >= len(array) {
return
}
temp := array[i]
array[i] = array[j]
array[j] = temp
}



接下来实现将n个元素排序成大根堆,并且交换根元素和第n个元素,并将第n各元素排出,继续比较n-1个元素构造大根堆的逻辑










1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

func (hs *HeapSort) sort(array []int) {
//每次循环后长度减少,因为每次循环最后元素都变为最大
for length := len(array); length > 1; length-- {
//最后一个非叶子节点索引
lastnode := length/2 - 1
//从最后一个非叶子节点一次从左到右,从下到上排序子树
//循环过后得到一个大顶堆(此时还不是大根堆)
for i := lastnode; i >= 0; i-- {
hs.adjustHeap(array, i, length)
}
//将堆顶元素放到末尾
hs.swap(array, 0, length-1)
fmt.Println(array)
}
}



ok,算法完成,可以进行测试了










1
2
3
4
5
6

func main() {
//数组初始化
array := []int{6, 1, 0, 5, 2, 9, 6}
hs := new(HeapSort)
hs.sort(array)
}



结果如下,打印的是每次排出元素后序列的结果










1
2
3
4
5
6

[6 5 6 1 2 0 9]
[0 5 6 1 2 6 9]
[2 5 0 1 6 6 9]
[1 2 0 5 6 6 9]
[0 1 2 5 6 6 9]
[0 1 2 5 6 6 9]



目前为止,堆排序介绍完了,欢迎下载源码

源码在阅读原文中


推荐阅读
  • 数据结构与算法习题replacementselectionsort(置换选择排序)TimeLimit:1000msMemoryLimit:65536kBDescrip ... [详细]
  • 开发笔记:快速排序和堆排序
    本文由编程笔记#小编为大家整理,主要介绍了快速排序和堆排序相关的知识,希望对你有一定的参考价值。快速排序思想:在partition中,首先以最右边的值作为划分值x,分别维护小于 ... [详细]
  • 阿里Treebased Deep Match(TDM) 学习笔记及技术发展回顾
    本文介绍了阿里Treebased Deep Match(TDM)的学习笔记,同时回顾了工业界技术发展的几代演进。从基于统计的启发式规则方法到基于内积模型的向量检索方法,再到引入复杂深度学习模型的下一代匹配技术。文章详细解释了基于统计的启发式规则方法和基于内积模型的向量检索方法的原理和应用,并介绍了TDM的背景和优势。最后,文章提到了向量距离和基于向量聚类的索引结构对于加速匹配效率的作用。本文对于理解TDM的学习过程和了解匹配技术的发展具有重要意义。 ... [详细]
  • LeetCode笔记:剑指Offer 41. 数据流中的中位数(Java、堆、优先队列、知识点)
    本文介绍了LeetCode剑指Offer 41题的解题思路和代码实现,主要涉及了Java中的优先队列和堆排序的知识点。优先队列是Queue接口的实现,可以对其中的元素进行排序,采用小顶堆的方式进行排序。本文还介绍了Java中queue的offer、poll、add、remove、element、peek等方法的区别和用法。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • Learning to Paint with Model-based Deep Reinforcement Learning
    本文介绍了一种基于模型的深度强化学习方法,通过结合神经渲染器,教机器像人类画家一样进行绘画。该方法能够生成笔画的坐标点、半径、透明度、颜色值等,以生成类似于给定目标图像的绘画。文章还讨论了该方法面临的挑战,包括绘制纹理丰富的图像等。通过对比实验的结果,作者证明了基于模型的深度强化学习方法相对于基于模型的DDPG和模型无关的DDPG方法的优势。该研究对于深度强化学习在绘画领域的应用具有重要意义。 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
  • JVM:33 如何查看JVM的Full GC日志
    1.示例代码packagecom.webcode;publicclassDemo4{publicstaticvoidmain(String[]args){byte[]arr ... [详细]
  • 生产环境下JVM调优参数的设置实例
     正文前先来一波福利推荐: 福利一:百万年薪架构师视频,该视频可以学到很多东西,是本人花钱买的VIP课程,学习消化了一年,为了支持一下女朋友公众号也方便大家学习,共享给大家。福利二 ... [详细]
  • PriorityQueue源码分析
     publicbooleanhasNext(){returncursor&amp;amp;lt;size||(forgetMeNot!null&amp;amp;am ... [详细]
  • 尾部|柜台_Java并发线程池篇附场景分析
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java并发-线程池篇-附场景分析相关的知识,希望对你有一定的参考价值。作者:汤圆个人博客 ... [详细]
author-avatar
xinweiss
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有