堆排序的自解
加入由一个无序数组:[9, 4, 8, 3, 5, 1, 2, 6, 7, 0]
思路:先将无序数组构建成一个完全二叉树:
----------------------------------------------正 文 分 割 线----------------------------------------------
什么是二叉树?
二叉树模型(圈里的数字代表着标号,而不是实际存放的数据):
二叉树的相关概念:
- 节点:树的基本组成部分,一个含有数据元素的******(不可描述)
- 节点的度:节点拥有的子节点的数量,如一个节点拥有两个子节点,则称该节点的度为2。也就是有几个孩子
- 完全二叉树:所有节点的度都小于等于2的树
- 子节点:节点的子树的根节点称为该节点的子节点,比如,节点8为节点4的子节点。也就是节点8叫节点4爸爸,节点4叫节点2爸爸,那节点8就应该叫节点2爷爷对吧,也是的,也就可以引申出节点8是节点2的子孙节点,
- 父节点:父子关系应该很容易懂吧,比如,节点4是节点8的父节点。
- 兄弟节点:有同一个父节点的众多节点互相称为兄弟节点。比如节点4和节点5为兄弟节点
- 树的深度:这棵树有几层,图示中的树有4层,深度也为4
什么是二叉树:
二叉数是只拥有两颗子树的树,两颗子树分别被称为“左子树“和”右子树”.
什么是堆?
堆是一个特殊的完全二叉树,并且;设有一个堆顶编号为1的含有 n 个元素的堆,总是有以下的几点特性:
- 第i个分支节点的子节点的序号总是2*i + 1和 2 * i + 2,
- 父节点总是不大于或者不小于自己的子节点,父节点总是不小于子节点的堆是一个大顶堆,反之则为一个小顶堆
- 这个堆的最深深度为 (log2n) + 1,所以第 i 个节点所在的层数也为 (log2n) + 1
- 堆里的第i层最多有 2^i - 1^个节点
----------------------------------------------正 文 回 归 线----------------------------------------
它组成的无序树(圈里的数字代表着存放的数据,而不是标号)如下:
从一个无序完全二叉树到一个大/小顶堆主要靠的就是调整这个堆。
流程解析:
从最后一个非叶子节点(数值为5的节点)开始,若父节点小于子节点,则互换他们两的位置,如下:
然后依次从右至左,从下到上进行,下一个便是数值为8的节点(这个节点没有什么变化),接下来是数值为 4 的节点:
但是这样子会导致 新的数值为 4 的节点乱掉,所以需要再对 这个 数值为4的节点进行调整。如图:
这样就可以经行下一步的调整了,然后我们继续对剩下的节点做相同的事情就ok啦。
import math
def adjust_heap(arr, i): # arr为队列,i为非叶子节点的编号
length = len(arr)
k = 2 * i + 1 # k为左孩子的下标
if i >= length/2: # 拥有子节点的才能继续往下走
return
if k + 1 < length: # 确定该右子节点是否存在
if arr[k] < arr[k + 1]: # 存在则比较左右子节点
k += 1 # 如果右子节点大于左子节点,则指向右子节点
print(i, k, arr[i], arr[k], sep = '*')
if arr[i] < arr[k]:
print(i,k, arr[i], arr[k],sep = '#')
arr[i], arr[k] = arr[k], arr[i] # 交换父子节点
print(i,k, arr[i], arr[k], sep = '#')
adjust_heap(arr, k) # 对交换的子节点进行修复
def build_big_heap(arr):
length = len(arr)
i = int(length/2) - 1
while i >= 0:
adjust_heap(arr, i)
i -= 1
if __name__ == "__main__":
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4]
build_big_heap(a)
print(a)