数据结构与算法 python--第五节 排序(七)堆排序

本文深入解析了堆排序算法,包括大根堆和小根堆的概念,以及如何通过堆排序实现数组的升序排列。文章详细介绍了堆排序的基本步骤,从构造初始堆到逐步调整堆结构,最终得到有序数组。

一、堆的概念

的结构可以分为大根堆和小根堆,是一个完全二叉树
完全二叉树:若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树
在这里插入图片描述
堆排序是根据堆的这种数据结构设计的一种排序,下面先来看看什么是大根堆和小根堆

大根堆和小根堆

大根堆:每个结点的值都大于其左孩子和右孩子结点的值;
小根堆:每个结点的值都小于其左孩子和右孩子结点的值。
如下图
在这里插入图片描述
转换成数组即为:
在这里插入图片描述
对于数组中索引为 i i i 的数,其父结点和左右孩子结点分别表示为:
父节点: ( i − 1 ) / / 2 (i-1) // 2 (i1)//2 (整除)
左子节点: 2 ∗ i + 1 2*i + 1 2i+1
右子节点: 2 ∗ I + 2 2*I + 2 2I+2

二 堆排序基本步骤

基本思想:

1.首先将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端

2.将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1

3.将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组

2.1 构造堆

将无序数组的根节点及其子节点构造成一个大根堆

def heapify(tree,n,i):
    """将数组变换成 堆"""
#     n = len(tree)
    if i >= n:
        return
    l_c = 2*i + 1
    r_c = 2*i + 2
    max = i
    if l_c < n and tree[l_c] > tree[max]:
        max = l_c
    if r_c < n and tree[r_c] > tree[max]:
        max = r_c
    if max != i:
        tree[i],tree[max] = tree[max],tree[i]
        heapify(tree,n,max)

测试:

tree = [4,5,3,10,1,2]
n = 6
heapify(tree,n,0)

[5, 10, 3, 4, 1, 2]
此时,只对最上面的一个子树进行了 堆 的构造,实际上 需要对所有的节点都进行重新排列,故而进行下一步:
从树的最后一个叶子节点的父节点开始做heapify(如下图中的 3)
在这里插入图片描述

def build_heap(tree,n):
    last = n - 1
    parent = (last-1) // 2
    for i in range(parent,-1,-1):
        heapify(tree,n,i)

测试:

tree = [4,5,3,10,1,2]
n = 6
heapify(tree,n,0)
print(tree)
build_heap(tree,n)
print(tree)

[5, 10, 3, 4, 1, 2]
[10, 5, 3, 4, 1, 2]

最后一步:将堆顶端的数(最大)与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1;然后将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组

def heap_sort(tree,n):
    build_heap(tree,n)
    for i in range(n-1,-1,-1): # for循环与while作用同,哪种表示都可以
#     i = n-1
#     while i >= 0:
        tree[i],tree[0] = tree[0],tree[i]
        heapify(tree,i,0)
#         i -= 1

测试:

tree = [4,5,3,10,1,2]
n = 6
heapify(tree,n,0)
print(tree)
build_heap(tree,n)
print(tree)
heap_sort(tree,n)
print(tree)

[5, 10, 3, 4, 1, 2]
[10, 5, 3, 4, 1, 2]
[1, 2, 3, 4, 5, 10]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值