python数据结构与算法—>NB三人组之堆排序
二叉树的顺序存储方式
堆由孩子找父亲(重要!):
若孩子节点下标为i,则父亲为(i-1)//2
完全二叉树:叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树
堆:一种特殊的完全二叉树
- 大根堆:一棵完全二叉树,满足任一节点都比其孩子节点大
- 小根堆:一棵完全二叉树,满足任一节点都比其孩子节点小
堆的向下调整:
当根节点的左右子树都是堆但自身不是堆时,可以通过一次向下的调整来将其变换成一个堆
调整前:
调整后:
堆排序过程
- 建立堆(建堆是从下往上)
- 得到堆顶元素,为最大元素
- 去掉堆顶,将堆最后一个元素放到堆顶,此时可通过一次调整重新使堆有序
- 堆顶元素为第二大元素
- 重复步骤3,直到堆变空
手写版:
时间复杂度:nlogn
import random
# 向下调整函数
def sift(li, low, high):
"""
:param li: 列表
:param low: 堆的根节点的位置
:param high: 堆的最后一个元素的位置
"""
i = low # i最开始指向根节点
j = 2 * i + 1 # j开始是左孩子
tmp = li[low] # 把堆顶存起来
while j <= high: # 只要j位置有数
if j + 1 <= high and li[j + 1] > li[j]: # j是左孩子, j+1是右孩子,如果右孩子有并且比较大
j = j + 1 # 指向右孩子
if li[j] > tmp:
li[i] = li[j]
i = j # 往下看一层
j = 2 * i + 1
else: # tmp更大,把tmp放到i的位置上
li[i] = tmp # 把tmp放到某一级领导位置上
break
else:
li[i] = tmp # 把tmp放到叶子节点上
"""
堆排序过程:
1. 建立堆
2. 得到堆顶元素,为最大元素
3. 去掉堆顶,将堆最后一个元素放到堆顶,此时可通过一次调整重新使堆有序
4. 堆顶元素为第二大元素
5. 重复步骤3,直到堆变空
"""
def heap_sort(li):
# --- 建堆 ---
n = len(li)
for i in range((n - 2) // 2, -1, -1): # 从(n-2)//2开始,倒着遍历到0(建堆先从叶子节点开始)
# i表示建堆的时候调整的部分的根的下标(sift的low参数)
sift(li, i, n - 1) # high是最后一个元素的下标
# --- 完成 ---
# 挨个出数
for i in range(n - 1, -1, -1):
# i指向当前堆的最后一个元素
li[0], li[i] = li[i], li[0] # 堆顶与最后一个元素做交换
sift(li, 0, i - 1) # i-1是新的high
return li
li = [i for i in range(10)] # 生成0~9的列表
random.shuffle(li) # 打乱列表顺序
print(li) # 打乱后的列表
print(heap_sort(li)) # 堆排序好的列表
python内置函数版:
import heapq # q--->queue 优先队列
import random
li = [i for i in range(10)]
random.shuffle(li)
heapq.heapify(li) # 建堆
for i in range(len(li)):
result = heapq.heappop(li) # 依次弹出
print(result, end=' ')