Python排序--堆排序

堆排序Heap Sort

  • 堆是一个完全二叉树
  • 每个非叶子结点都要大于或者等于其左右结点的值称为大顶堆
  • 每个非叶子节点都小于或者等于其左右结点的值称为小顶堆
  • 根结点一定是大顶堆中最大的值,一定是小顶堆中最小的值

大顶堆

  • 完全二叉树的每个非叶子结点都要大于或者等于其左右孩子结点的值称为大顶堆
  • 根结点一定是大顶堆中的最大值
    这里写图片描述

小顶堆

  • 每个非叶子节点都小于或者等于其左右结点的值称为小顶堆
  • 根结点一定是小顶堆中的最小值
    这里写图片描述

堆排序实现原理

堆排序原理

  1. 构建完全二叉树
    1. 待排序数字为 6,5,3,1,8,7,2,4
    2. 构建一个完全二叉树存放数据,并根据性质5对元素编号,放入顺序的数据结构中
    3. 构建一个列表为[0,6,5,3,1,8,7,2,4]
  2. 构建大顶堆-排序核心算法
    1. 度数为2的结点,如果它的左右孩子结点的最大值比它大,将这个最大值和该结点交换
    2. 度数为1的结点,如果它的左孩子大于它,则交换
    3. 如果结点被交换到新的位置,还需要和其孩子结点重复上面的过程
    4. 从完全二叉树的最后一个结点的双亲结点开始,即最后一层的最右边叶子结点的父节点开始
    5. 接点数为n,则起始结点的编号为n//2(性质5)
    6. 从起始结点向坐找其同层结点,到头后再从上一层的最右边开始继续向左逐个查找,直至根结点
    7. 确保每个结点的都比其左右孩子的值大
  3. 排序
    1. 将大顶堆根结点的最大值和最后一个叶子结点交换,最后一个叶子结点成为最大值,将这个叶子结点排除在待排序结点之外
    2. 从根结点开始(交换后的新的根结点),重新调整为大顶堆后,重复上一步

代码实现

这里写图片描述
生成列表

这里写图片描述
heap_main 函数实现了单次比较交换
这里写图片描述

loop_heap 函数实现了重复交换排序,直至形成大顶堆
这里写图片描述
swop 函数每次都将大顶堆的堆顶和最后一个结点交换,然后排除最后一个元素
在继续调整形成大顶堆
不断的重复,直至剩余一个为排序的元素

总结

  • 利用堆性质的一种选择排序,在堆顶选出最大值或者最小值
  • 时间复杂度
    • 堆排序的时间复杂度为O(nlogn)
    • 由于堆排序对原始数据的排序状态并不敏感,因此它无论原始排序状态的好坏,时间复杂度均为O(nlogn)
  • 空间复杂度
    • 只使用了一个交换空间,复杂度为O(1)
  • 稳定性
    • 不稳定的排序算法

扩展

打印列表形式,不容易发现进行交换的改变
所以通过一个函数将数据格式化输出成二叉树的形式
这里写图片描述

完整代码

origin = [0,6,5,3,1,8,7,2,4]    #为了和编号对应,添加元素 0 在首位
total = len(origin) - 1 #初始待排序元素个数
import math


def print_tree(arr, unit_width=2):
    length = len(arr)
    depth = math.ceil(math.log2(length))

    index = 1

    width = 2 ** depth - 1
    for i in range(depth):
        for j in range(2 ** i):
            print('{:^{}}'.format(arr[index], width * unit_width), end=' '*unit_width)
            index += 1
            if index >= length:
                break
        width = width // 2
        print()

print('排序前')
print('________________________________')
print_tree(origin)


def heap_main(n,i,arr):
    '''

    :param n: 待比较数的个数
    :param i: 当前节点的下标
    :param arr: 待排序数据
    :return: None
    '''
    while 2 * i <= n:
        #   孩子结点判断 2i 为左孩子,2i+1 为右孩子
        lkid_index = 2 * i
        max_kid_index = lkid_index
        if n > lkid_index and arr[lkid_index + 1] > arr[lkid_index]:
            # n > 2i 说明有有孩子,判断左右孩子谁最大
            max_kid_index = lkid_index + 1

        if arr[max_kid_index] > arr[i]:
            # 最大孩子和父节点比较
            arr[i], arr[max_kid_index] = arr[max_kid_index], arr[i]
            i = max_kid_index   #被交换后,需要判断是否还需要进行调整

        else:
            break


def loop_heap(total, arr):
    for i in range(total//2, 0, -1):
        # 找出所有的结点,从最后的结点开始,直到根结点
        heap_main(total, i, arr)
    return arr


def swop(total, arr):
    while total > 1: #重复交换和排序,直至待排序的元素剩余一个
        arr[1], arr[total] = arr[total], arr[1]
        # 堆顶和最后一个结点交换
        total -= 1
        heap_main(total,1,arr)
        # 对破坏都的队进行重新排序
    return arr


loop_heap(total, origin)
swop(total, origin)
print('排序后')
print('________________________________')
print_tree(origin)

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值