堆排序
打印树
- 居中对齐方案
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)])
- 投影栅格实现
序号 | i | 前空格 | 元素间 |
---|
1 | 3 | (7=2**3-1) | (2*前空格数+1) |
2 | 2 | (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)
```
代码实现
核心算法-堆结点的调整
- 结点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)
```
总结
- 是利用对性质的一种选择排序, 在堆顶选出最大值或者最小值
- 时间复杂度O(nlogn)
- 由于堆排序对原始记录的排序状态并不敏感,因此它无论最好最坏时间复杂度均为O(nlogn)
- 空间复杂度:只是使用了一个交换用的空间,空间复杂度就是O(1)
- 稳定性:属于不稳定的排序算法
- 堆是完全二叉树