首页 > 编程语言 >【算法实践】他山之石,可以攻玉--利用完全二叉树快速实现堆排序

【算法实践】他山之石,可以攻玉--利用完全二叉树快速实现堆排序

时间:2022-12-21 20:33:36浏览次数:68  
标签:arr 可以攻玉 -- 堆排序 二叉树 数组 排序 节点

前言

什么是堆

堆是一种数据结构,它是完全二叉树或者是近似完全二叉树的一种数据结构,树中每个结点的值都不小于(或不大于)其左右孩子结点的值。

何为完全二叉树

完全二叉树是一种特殊的二叉树,完全二叉树是除了最后一层之外的其他每一场层都被完全填充,叶子节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树,也就是说所有节点都保持向左对齐。如果想了解更多关于二叉树的介绍,可参考之前写的一篇文章《​​【数据结构实践】手把手带你快速实现自定义二叉树​​》

对完全二叉树来说,较为简洁的实现方法就是使用数组来存储完全二叉树。这样结点就按层序存储于数组中,其中第一个结点将存储于数组中的1号位,并且数组i号位表示的结点的左孩子就是2i号位,而右孩子则是(2i+1)号位。

什么是堆排序

堆排序与快速排序,归并排序一样都是时间复杂度为O(N*logN)的几种常见排序方法,堆排序是将数据看成完全二叉树,然后根据完全二叉树的特性来进行排序的一种排序算法,这有点草船借箭的妙用。正所谓他山之,石可以攻玉。在堆排序中,堆具体可分为最大堆和最小堆,也有人称他们为大顶堆和小顶堆,如果父节点的值大于或等于孩子结点的值,那么称这样的堆为最大堆,这时每个节点的值都是以其为根结点的子树的最大值,即最大堆要求节点的元素都大于其对应的叶子节点,通常被用来进行升序排序,如果父节点的值小于或等于孩子结点的值,那么称这样的堆为最小堆,这时每个节点的值都是以其为根结点的子树的最小值,即最小堆要求节点的元素都小于其对应的叶子节点,通常被用于降序排序。堆一般用于优先队列的实现,而优先队列默认情况下使用的是最大堆。

小结:

最大堆:每个节点的值都大于或者等于它的左右子节点的值。

最小堆:每个节点的值都小于或者等于它的左右子节点的值。

如下图:

【算法实践】他山之石,可以攻玉--利用完全二叉树快速实现堆排序_图解算法

【算法实践】他山之石,可以攻玉--利用完全二叉树快速实现堆排序_堆排序_02

以最大堆为例,利用最大堆的结构特点,每个最大堆的根节点必然会是数组中最大的元素,构建一次最大堆,就可以获取数组中最大的元素。剔除最大元素后,反复构建剩下的数字为最大堆,获取根元素,最终保证数组有序。最大堆和最小堆是对称关系,学会其中一种即可

综上所述我们可以得出以下性质:

对于最大堆:arr[i] >= arr[2i + 1] && arr[i] >= arr[2i + 2]

对于最小堆:arr[i] <= arr[2i + 1] && arr[i] <= arr[2i + 2]

【算法实践】他山之石,可以攻玉--利用完全二叉树快速实现堆排序_结点_03

堆排序的算法设计

具体步骤如下:

  1. 将待排序的数组构造成一个最大堆,根据最大堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素
  2. 数组转换成最大堆之后,将堆顶元素取出,然后将剩下的节点重新构建为最大堆
  3. 重复步骤2,如此反复,直到剩余数只有一个时结束,从第一次构建大顶堆开始,每一次构建,我们都能获得一个序列的最大值
  4. 交换数据,最后,就得到一个有序的数组了

【算法实践】他山之石,可以攻玉--利用完全二叉树快速实现堆排序_数组_04

代码实现

1.定义最大堆函数,传入当前节点的位置,左孩子位置是2*root+1.调整位置使堆顶元素最大

def siftdown(arr, node, end): #arr[i] >= arr[2i + 1] && arr[i] >= arr[2i + 2]
root = node # 当前节点的位置
while True:
# 从root开始对最大堆调整
child = 2 * root +1 #左(left)孩子的位置
if child > end:
break
# 找出两个child中较大的一个
if child + 1 <= end and arr[child] < arr[child + 1]: #如果左边小于右边
child += 1 #左右两孩子中选择较大者
if arr[root] < arr[child]:
# 最大堆小于较大的child, 交换顺序
tmp = arr[root]
arr[root] = arr[child]
arr[child]= tmp
root = child #
else:
# 无需调整的时候, 跳出循环
break

2.定义堆排序函数,传递待排数组,逐次遍历,

def heap_sort(arr):
# 从最后一个有子节点的孩子开始调整最大堆,不断的缩小调整的范围直到第一个元素
first = len(arr) // 2 -1
for i in range(first, -1, -1):
siftdown(arr, i, len(arr) - 1)
# 将最大的放到堆的最后一个, 堆-1, 继续调整排序
for end in range(len(arr) -1, 0, -1):
arr[0], arr[end] = arr[end], arr[0]
siftdown(arr, 0, end - 1)

验证堆排序算法:

def main():
array = [16,10,15,8,9,12,14,6]
print("原数组: ",array)
heap_sort(array)
print("排序后数组: ",array)
if __name__ == "__main__":
main()

执行结果:

【算法实践】他山之石,可以攻玉--利用完全二叉树快速实现堆排序_数组_05

总结

一般需要排序的数据都存放于数组中,而堆排序使用了堆这个数据结构,相当于将堆嵌入到包含了序列的数组中,然后通过交换数组中的数据来进行排序,可以说是强行在数组中使用了堆结构。堆排序一开始需要将n个数据存进堆里,所需要时间为O(nlogn)。排序过程中,堆从空堆的状态开始,逐渐被数据填满,由于堆的高度小于log2n,所以插入1个数据所需要的时间是O(logn)。每轮取出最大的数据并重构堆需要的时间为O(logn),由于总共n轮,因此重构后排序的时间也是O(nlogn)。故整体看来堆排序的时间复杂度为O(nlogn),虽然堆排序整体运行效率不错,但是堆这个结构相对复杂,所以实现起来也较为困难。

标签:arr,可以攻玉,--,堆排序,二叉树,数组,排序,节点
From: https://blog.51cto.com/micai01/5960107

相关文章

  • 云上办公,且看华为云桌面如何加速企业数字化发展之路?
    如今,远程办公的企业员工逐渐成为主要的劳动力,企业将接受并选择更灵活、智能和安全的数字工作空间解决方案,以改善员工的工作体验,提高工作效率,保持业务连续性。而云桌面解决方......
  • Vue3.0 生命周期
    所有生命周期钩子的this上下文都是绑定至实例的。beforeCreate:在实例初始化之后、进行数据帧听和事件/侦听器的配置之前同步调用。created:实例创建完成,主要包括数据帧听......
  • 数字云办公连续7年领跑,华为云桌面优势突显!
    随着近几年企业工作方式的转型,云上办公的市场规模越来越大,吸引了众多传统本地办公企业迈向云上办公,包括远程办公、混合办公、分支机构办公等场景,在线协作成为各行各业高效办......
  • Application资源规范
    ApplicationCRD的spec字段主要嵌套如下几个字段◼source<object>:配置仓库及相关的配置访问及使用方法;支持如下几种类型◆Kubernetes原生资源配置:直接于配置仓库中获取目......
  • 数字化办公?选云桌面就对了!
    近几年来,随着企业信息化程度不断提升,越来越多的中小企业开始采用远程办公方式来提高工作效率,而这种方式对数据安全要求很高,“云桌面”恰好符合企业安全办公需求。在企业IT管......
  • 【编程实践】认识爬虫并手把手带手实现新闻网站的爬取
    前言什么是爬虫网络爬虫(WebSpider)又叫网络蜘蛛,或者网络机器人(在FOAF社区中间,更经常的称为网页追逐者),正如他的英文名一样,很形象的一个名字。把互联网比喻成一个蜘蛛网,......
  • 云办公成趋势,华为云桌面全方位保障企业安全
    近几年来,云桌面作为一种新型企业级应用服务模式,已经被逐渐被企业所接受。因此,如何让云桌面能够高效稳定地运行也成为众多IT厂商关注的焦点之一。为了更好地为广大用户提供便......
  • 云上办公,还得是华为云桌面的一站式云上工作站
    其实,利用云桌面来实现云上办公并不是一个新形式,早在2010年左右就已经存在了。尤其在前两年,疫情爆发,企业和工作人员通过云桌面,完成了居家隔离状态下的工作任务。如今,云桌面已......
  • Pandas中高效的选择和替换操作总结
    使用正确的工具和技术来最大限度地利用数据是很重要的。Pandas是数据操作、分析和可视化的重要工具,有效地使用Pandas可能具有挑战性,从使用向量化操作到利用内置函数,这些最佳......
  • 应该掌握的HTTP状态码
    状态码的职责是当客户端向服务器端发送请求时,描述返回的请求结果。借助状态码,用户可以知道服务器端是正常处理了请求,还是出现了错误。2XX成功,2XX的响应结果表明请求被正常处......