• 堆排序


    此堆非彼堆,先简单说一下概念和思路。

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

    构建完全二叉树

    - 待排序数字为 30,20,80,40,50,10,60,70,90
    - 构造一个列表为 [0,30,20,80,40,50,10,60,70,90]

    构建大顶堆

    - 度数为2的结点A,如果它的左右子结点的最大值比它大,将这个最大值和该结点交换
    - 度数为1的结点A,如果它的子节点的值大于它,则交换
    - 如果结点A被交换到新的位置,还需要和其子结点重复上面的过程

    起点的选择

    - 从完全二叉树的最后一个结点的父结点开始
    - 结点数为n,则起始结点的编号为 n//2

    下一个节点选择

    - 从起始结点开始向左找其同层结点,到头后再从上一层的最右边结点开始向左逐个查找,直至根结点

    排序

    - 将大顶堆根结点这个最大值和最后一个叶子结点交换,那么最后一个叶子结点就是最大值,将这个叶子结点排除在待排序结点之外
    - 从根结点开始(新的根结点),重新调整为大顶堆后,重复上一步

    1、堆排序是利用堆性质的一种选择排序,在堆顶选出最大值或者最小值
    2、时间复杂度为O(nlogn)
    3、由于堆排序对原始记录的排序状态并不敏感,因此它无论是最好、最坏时间复杂度均为O(nlogn)
    4、不稳定的排序算法(在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的,否则称为不稳定的)

    import math
    
    # 构建完全二叉树
    origin = [0, 30, 20, 80, 40, 50, 10, 60, 70, 90]
    
    total = len(origin) - 1
    
    # 打印树
    def print_tree(origin, unit_width=2):
        length = len(origin)
        index = 1
        # 由于前面补0了,原本应该是math.ceil(math.log2(len(origin)+1)),这里减一
        depth = math.ceil(math.log2(length))
        sep = ' ' * unit_width
        for i in range(depth-1, -1, -1):    # range(3,-1,-1) [3,2,1,0]
            # 前半部分空格数 7,3,1,0
            pre = 2**i - 1
            print(sep * pre, end='')
            # 偏移量,1,2,4,8
            offset = 2 ** (depth - i - 1)
            # 切片获取数据,origin[1,2],origin[2,4],origin[4,8],origin[8,16]
            line = origin[index: index+offset]
            # 元素间距,0,7,3,1
            intervalspace = sep * (2*pre + 1)
            print(intervalspace.join(map(str, line)))
            index += offset
    
    # 最大堆调整,单个节点调整
    def heap_adjust(total, i, origin):
        """
        total: 节点总数
        i: 当前节点的索引
        """
        #               30
        #       20              80
        #   40      50      10      60
        # 70  90
        while 2*i <= total:
            # 2i为左子节点,2i+1为右子节点
            lchild_index = 2*i
            max_child_index = lchild_index
    
            # total>2i说明还有右子节点
            if total > lchild_index and origin[lchild_index + 1] > origin[lchild_index]:
                max_child_index = lchild_index + 1
    
            # 和子树的根节点比较
            if origin[max_child_index] > origin[i]:
                origin[i], origin[max_child_index] = origin[max_child_index], origin[i]
                i = max_child_index
            else:
                break
    
    # 构建大顶堆
    def max_heap(total, origin):
        # 从i=total//2开始,到堆顶节点,依次调整
        for i in range(total//2, 0, -1):    # range(4,0,-1) [4,3,2,1]
            heap_adjust(total, i, origin)
        return origin
    
    max_heap(total, origin)
    #               90
    #       70              80
    #   40      50      10      60
    # 20  30
    
    # 排序
    def sort(total, origin):
        while total > 1:
            # 堆顶和最后一个节点交换
            origin[1], origin[total] = origin[total], origin[1]
            # 每交换一次就固定一个数
            total -= 1
            if total == 2 and origin[total] >= origin[total - 1]:
                break
            # 这时只有堆顶节点不平衡,调整堆顶节点即可
            heap_adjust(total, 1, origin)
        return origin
    
    print_tree(sort(total, origin))
    #               10
    #       20              30
    #   40      50      60      70
    # 80  90
    

    参考:
    https://zh.wikipedia.org/wiki/堆排序

  • 相关阅读:
    Array.sort源码
    单例模式
    nio理解
    xoa中范型的应用
    mybatis 一对多映射 xml
    zookeeper
    java final
    spring controller里面返回JSONObject与返回String的不同
    synchronized的可重入性
    nio select poll epoll
  • 原文地址:https://www.cnblogs.com/keithtt/p/9677781.html
Copyright © 2020-2023  润新知