堆的相关知识:
堆?
1.堆是一种完全二叉树
2.每个节点的值总是大于等于(大根堆)或者小于等于(小根堆)子节点的值.
完全二叉树?
除了最后一层外, 每一层都被完全填充, 且所有节点都向左对齐.
大根堆:
每个节点都大于等于子节点
小根堆:
每个节点都小于等于子节点
堆排序需要使用两个函数. 实现以下功能
1. 构建堆,以及实现堆内部对比以及调换.
2. 堆排序的调用, 以及堆大小的控制
# 堆排序_Python实现 # 堆最大化 def heapify(li, i, n): # 父节点位置n largest = i # 子节点位置,左,右 left = i * 2 + 1 right = i * 2 + 2 if left < n and li[left] > li[largest]: largest = left if right < n and li[right] > li[largest]: largest = right if largest != i: li[largest], li[i] = li[i], li[largest] # 显示变动 # print(li) # 当位置发生变动的时候, 递归调用, 重新排位 heapify(li, largest, n) # 因为是直接针对list列表内进行排序, 则不需要返回列表 # 堆排序 def heap_sort(li): n = len(li) # 自下至上建堆 for i in range(n // 2 - 1, -1, -1): heapify(li, i, n) for i in range(n - 1, -1, -1): li[0], li[i] = li[i], li[0] # 该循环中,i有两个含义:1.代表当前堆中最后一个节点的下标(转化后为最大值).2.代表下次循环中堆的大小(相当于省却n-1步骤.) # 这样, 在进行堆计算的时候, 就把已经调换至最后一个的最大值忽略掉, 直接进行下次排序. heapify(li, 0, i) # 在传入下标的时候, 只需要传入0, 也就是从堆最后交换过来的元素(因为上次排序好之后只有该元素不符合顺序).然后交给heapify来重新梳理即可. return li list = [1, 55, 98984, 65, 165, 356, 54, 3, 645, 74, 64, 35] list = heap_sort(list) print(list)
堆结构特点:
任何一排的元素数量,等于上面元素总和+1.
注意点:
在构建堆的时候,因为需要反顺序, range的时候需要写入步长-1
在写堆排序的时候, 最需要注意的点, 在于主节点下标的数值, 以及堆大小.
逻辑流程:
在构建堆的时候, 从最底部的父节点开始,这样可以一次构建成功,多次向下转换.
根据堆的特点, 计算出, 我们的最后一个父节点应该是n//2-1. 此处应使用整除,避免浮点.
在堆化逻辑内
每个父子节点都会比较大小. 一旦子节点大于父节点,则交换.
且, 交换下去的节点,重新与它下面的子节点对比,如果它仍然比现在位置的子节点小, 继续交换.
直到无法交换为止. 这个过程完成了堆的排序.
构建完成后, 当前堆的第一个元素一定是列表中最大的.
在后面调用堆排序的时候, 一般通过交换第一个和最后一个元素,来把最大元素保留在列表最后.
并且使堆大小-1, 这样交换过去的最大值元素就不在计算在堆内.
而新交换过来的元素此时为首位元素. 这个时候对首位以及它的子节点进行堆化整理, 能够重新使堆变得整齐起来. (这元素比子节点小,往下挪,挪了之后还小?继续往下挪! ...)
这样, 重新堆化过后的首位元素, 依旧是堆内所有元素中最大的.
那么, 继续~
交换头尾, 堆大小减一, 重新排序..
交换头尾, 堆大小减一, 重新排序...