堆排序
# 思路
'''
1.构造堆
2.挨个出数(每次选出一个最大(小)的数)
时间复杂度:nlogn
'''
# 构造堆
def sift(li, low, high):
'''
:param li:列表
:param low:堆的根节点
:param high:堆的最后一个叶子节点
:return:
'''
i = low # i最开始指向根节点
j = 2 * i + 1 # j一开始指向i的左孩子
tmp = li[low] # 堆顶元素
while j <= high: # 只要j不越界
# j指向i的子节点中更大的一个节点
if j + 1 <= high and li[j + 1] > li[j]: # 如果右孩子存在且比左孩子大,那么j就指向右孩子
j = j + 1
# 现在判断应该把i放在哪个地方
if li[j] > tmp:
li[i] = li[j]
# 更新i和j的位置
i = j
j = 2 * i + 1
else:
li[i] = tmp
break
else:
li[i] = tmp # 如果是因为j越界结束的循环,tmp应该放在i的位置
@clocked
def heap_sort(li):
# 构造堆
n = len(li)
# 从最后一个含有叶子节点的节点开始构造
'''
由父节点推子节点: 父:n ---> 左孩子:2n+1 右孩子:2n+2
由子节点推父节点: 左孩子/右孩子:n 父:(n-1)//2
'''
# 依次构造堆
for i in range((n - 1 - 1) // 2, -1, -1):
# i表示需要构建的部分的堆的根节点
sift(li, i, n - 1)
# 一个一个出
for i in range(n - 1, -1, -1):
li[0], li[i] = li[i], li[0]
sift(li, 0, i - 1)
topk问题
#思路
'''
1.首先选列表的前k个数建堆
2.从列表的第k+1个数开始遍历列表
如果当前元素大于堆顶元素,就和堆顶元素进行交换
3.挨个出数得到排序好的topk
'''
# 构造堆
def sift(li, low, high):
'''
:param li:列表
:param low:堆的根节点
:param high:堆的最后一个叶子节点
:return:
'''
i = low # i最开始指向根节点
j = 2 * i + 1 # j一开始指向i的左孩子
tmp = li[low] # 堆顶元素
while j <= high: # 只要j不越界
# j指向i的子节点中更大的一个节点
if j + 1 <= high and li[j + 1] < li[j]: # 如果右孩子存在且比左孩子大,那么j就指向右孩子
j = j + 1
# 现在判断应该把i放在哪个地方
if li[j] < tmp:
li[i] = li[j]
# 更新i和j的位置
i = j
j = 2 * i + 1
else:
li[i] = tmp
break
else:
li[i] = tmp # 如果是因为j越界结束的循环,tmp应该放在i的位置
@clocked
def topk(li, k):
heap = li[:k]
# 建堆
for i in range((k - 2) // 2, -1, -1):
sift(heap, i, k - 1)
# 遍历
for i in range(k, len(li) - 1):
if li[i] > heap[0]:
heap[0] = li[i]
sift(heap, 0, k - 1)
# 出数
for i in range(k - 1, -1, -1):
heap[0], heap[i] = heap[i], heap[0]
sift(heap, 0, i - 1)
return heap