题目描述
给定一个数组,找出其中最小的K个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。如果K>数组的长度,那么返回一个空的数组
输入输出
输入:[4,5,1,6,2,7,3,8],4
返回值:[1,2,3,4]
解题思路
最大堆的特性是根节点的值比左右节点的值大,并且是完全二叉树的结构。我们可以用序列中的数来创建最大容量为k的最大堆,每次新的插入都保证k的容量,并且满足最大堆中k个元素是最小的。
- 首先,要完成最大堆的创建;
最大堆创建的过程中,每次插入的值都是放在堆的最后一个位置,每次的插入都需要去比较插入的数与其父节点的数的大小,如果插入的值比其父节点大的话,需要两项做交换,这样比较变换直到当前的索引指向最大堆的根节点。 - 然后需要定义最大堆的调整,因为当创建的最大堆容量到达k时,每次存入都要去调整使得每次的堆都是一个最大堆的形式,这样后续插入的数与根节点的值进行比较;
调整最大堆的过程是,非常插入的数与最大堆的根节点值做比较,如果插入的数的值小于根节点的值就进行两项的交换;然后再判断此时根节点的值与其左右子节点的值的大小,如果比左右子节点的值小的话,则需要与其中最大的数进行交换,依次进行直到指向堆的最后一个节点。 - 定义了创建和调整最大堆后,在找出最小k个数中调用;
- 如果此时的堆的容量不等于k,则直接将序列中的数插入堆中,直到成为容量为k的堆;
- 这是再往堆中插入元素时,每次的插入都要调整堆,使其成为最大堆
- 序列遍历完成后,容量为k,节点数为最小的最大堆也完成,需要将堆中元素排序后返回。
代码示例
# coding:utf-8
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
# 创建最大堆,最大堆用数组表示,
maxHeap =[]
# 创建或者插入最大堆
# 要传入一个num的值
def creatMaHeap(num):
# 向最大堆中插入一个值,要判断是否大于其父节点,
# 大于其父节点,就交换,直到为根节点(索引为0)
maxHeap.append(num) # 加到堆最后
currentIndex = len(maxHeap) - 1 # 当前数在堆中的索引
# 当当前节点不是根节点(索引不为0)时,
while currentIndex != 0:
parentIndex = (currentIndex - 1) // 2 # 当前节点的父节点索引
# 如果当前节点值大于其父节点的值,做替换
if maxHeap[parentIndex] < maxHeap[currentIndex]:
maxHeap[currentIndex], maxHeap[parentIndex] = maxHeap[parentIndex], maxHeap[currentIndex]
# 如果当前节点值小于父节点的值,不用做替换,直接跳出循环
else:
break
# 调整最大堆 头结点发生改变
# 输入一个num与堆顶元素做替换
def adjustMaxHeap(num):
# 如果输入的num小于堆顶元素的值,就进行替换,替换后调整堆结构使其继续满足最大堆的特性
if num < maxHeap[0]:
maxHeap[0] = num
maxHeapLen = len(maxHeap)
index = 0
# 当index小于最大索引时,(最后一个节点)
while index < maxHeapLen:
# 计算出左右节点的索引值
leftIndex = index * 2 + 1
rightIndex = index * 2 + 2
'''判断与哪个节点做替换'''
# 首先确定左右节点中值最大的节点,
largerIndex = 0 # 保存左右节点的最大值的索引
# 然后再用这个最大值的节点与父节点做替换
# 如果右节点的索引小于最大堆的节点长度(存在右节点,就一定存在左节点)
# 比较左右节点值的大小;将最大的所在索引赋值给largerIndex
if rightIndex < maxHeapLen:
if maxHeap[rightIndex] < maxHeap[leftIndex]:
largerIndex = leftIndex
else:
largerIndex = rightIndex
# 如果左节点索引小于最大堆节点长度(存在左节点,不存在右节点)
elif leftIndex < maxHeapLen:
# 因为只有左节点,所以直接将左节点的索引给最大值索引
largerIndex = leftIndex
# 既没有左节点,又没有右节点的情况(走到最底)
else:
break
# 找到左右节点中的最大值索引后,与父节点的值进行比较
# 如果左右节点(下面的节点)中的最大值比父节点(上面的)的值大,则做替换,保持最大堆特性
if maxHeap[index] < maxHeap[largerIndex]:
# 交换两个节点值的位置
maxHeap[index], maxHeap[largerIndex] = maxHeap[largerIndex], maxHeap[index]
# 将当前最大值索引赋给index,继续接下来的替换
index = largerIndex
tinputLen = len(tinput)
# 边界条件:如果输入的序列长度不足k,返回空
if tinputLen < k or k <= 0:
return []
# 循环遍历序列
for i in range(tinputLen):
# 当堆中的节点数小于k时,创建堆
if i < k:
creatMaHeap(tinput[i])
# 当堆中的节点数为k时,每加入一个数,调整堆
else:
adjustMaxHeap(tinput[i])
maxHeap.sort()
return maxHeap