一、引入
问题:目前共有n个数,设计算法得到前k大的数。(m<n)
解决思路:
- 排序后切片:O(n*logn+m) = O(n*logn)
- 排序LowB三人组:O(mn) 例如冒泡排序,交换m次,即可取前m大的数字;对于插入排序,维护一个长度为100的列表,将其排好序,再依次遍历其他数,将插入的数字放入其中,后面的数字往后挪;选择排序我们选择100次。
- 堆排序:O(nlogk)
二、堆排序解决思路
- 取列表前k个元素建立一个小根堆(从小到大),其堆顶就是目前第K大的元素。
- 依次向后遍历原列表,对于列表中的元素,如果小于堆顶,则忽略该元素;如果大于堆顶,则将堆顶更换为该元素,并且对堆进行一次调整。
- 最后能确保小根堆是我们需要的前K大的数。
- 遍历列表的所有元素后,倒序(因为是小根堆)弹出列表。
示例代码如下:
import random
# 调整为小根堆
def sift(li, low, high): # 向下调整函数;初始条件应该为除了根节点,下面的子堆默认为有序的
i = low # i最开始指向根节点
j = 2 * i + 1 # 左孩子
tmp = li[low] # 将堆顶元素存起来
while j <= high: # 只要j上面有数,没跑到结构外
if j + 1 <= high and li[j + 1] < li[j]: # 存在右孩子,且右孩子比左孩子大;不能交换
j = j + 1 # j指向有孩子
if li[j] < tmp:
li[i] = li[j] # 换数
i = j # 往下看一层
j = 2 * i + 1
else: # 此时tmp更大,将tmp放回原位