堆排序

堆排序

在这里插入图片描述

打印树

  1. 居中对齐方案
    import math
    
    def print_tree(array, unit_width=2):
    
        length = len(array)
        depth = math.ceil(math.log2(length + 1))# 树的深度
        index = 0
        width = 2 ** depth - 1 #行宽,最深的行,数字加空白占位数
    
        for i in range(depth):# 0 1 2 3
            for j in range(2 ** i):
                print('{:^{}}'.format(array[index], width * unit_width), end=' ' * unit_width)
                index += 1
                if index >= length:
                    return
            width = width // 2
            print()# 控制换行
    
    print_tree([x + 1 for x in range(28)])
    
  2. 投影栅格实现
序号i前空格元素间
13(7=2**3-1)(2*前空格数+1)
22(3=2**2-1)(7=2*3+1)
依次类推
```
import math

origin = [0, 30, 20, 80, 40, 50, 10, 60, 70, 90]

def print_tree(array, unit_width=2):

    length = len(array)
    index = 1
    depth = math.ceil(math.log2(length))# 因为使用时前面补零了,不然应该是math.ceil(math.log2(length+1))
    space = ' ' * unit_width

    for i in range(depth-1, -1, -1):
        pre = 2 ** i - 1
        print(pre * space, end='')# 前置空格
        offset = 2 ** (depth - i - 1)
        line = array[index:index+offset]#取数字
        interval = (2 * pre + 1) * space # 间隔的空格
        print(interval.join(map(lambda x: '{:2}'.format(x), line)))

        index += offset

print_tree(origin)
```

代码实现

核心算法-堆结点的调整

  1. 结点A,在其左右孩子中找出最大值后与之交换位置, 若结点A被交换到新位置,重复以上步骤

完整代码

```
#为了和编码对应, 增加一个无用的0在首位
origin = [0, 30, 20, 80, 40, 50, 10, 60, 70, 90]
total = len(origin) - 1#初始待排元素个数, 即n

def heap_adjust(n, i, array:list):
          
    调整当前结点(核心算法)
    调整结点的起点在 n // 2, 保证所有调整的结点都有孩子结点
    :param n:待比较数个数
    :param i:当前结点的下标
    :param array:待排序数据
    :return None
   
    while 2 * i <= n:
        #孩子结点判断,2i为左孩子, 2i+1为右孩子
        lchild_index = 2 * i
        max_child_index = lchild_index #2i #先假定左孩子大,不确定是否有右孩子
        if n > lchild_index and array[lchild_index + 1] > array[lchild_index]:#说明还有右孩子,并且右孩子大于左孩子

            max_child_index = lchild_index + 1 #2i+1

        #和子树的根结点比较
        if array[max_child_index] > array[i]:
            array[i], array[max_child_index] = array[max_child_index], array[i]
            i = max_child_index  # 被交换后还需要判断是否需要调整
        else: #否则,当前子树根节点最大,无需调整,直接结束
            break

#构建大顶堆,大根堆(或者小顶堆)
    
    1. 起点的选择:从最下层最右边叶子结点的父结点开始,由于构造了一个前置的0,   
    所以编号和索引正好相对应,但元素个数等于长度减1
    2. 调整好的大顶堆一定满足:最大的在第一层, 第二层一定有一个次大的
    

def max_heap(total, array:list):
    for i in range(total // 2, 0, -1):
        heap_adjust(total, i, array)
    return array


#排序
   
    1. 数组->调整为大顶堆->堆顶的元素与最后一个元素->剔除最后一个元素->调整为大顶堆(如此循环往复)
    2. 剩余两个元素时, 如果最后一个较大, 则无需调整 
    
def sort(total, array:list):
    while total >1:
        array[1], array[total] = array[total], array[1]#堆顶和最后一个结点交换
        total -= 1
        if total == 2 and array[total] >= array[total - 1]:#当剩下堆顶和最后两个元素,且堆顶较小则退出完成排序
            break
        heap_adjust(total, 1, array)
    return array

max_heap(total, origin)
sort(total, origin)
print(origin)
```

总结

  1. 是利用对性质的一种选择排序, 在堆顶选出最大值或者最小值
  2. 时间复杂度O(nlogn)
  3. 由于堆排序对原始记录的排序状态并不敏感,因此它无论最好最坏时间复杂度均为O(nlogn)
  4. 空间复杂度:只是使用了一个交换用的空间,空间复杂度就是O(1)
  5. 稳定性:属于不稳定的排序算法
  6. 堆是完全二叉树
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值