版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
问题描述:
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
思路:
这个算是面试中常见题了,这里总结一下解决方法(注意判断k的合法性):
1、全部升序排序,输出前k个数字,最小时间复杂度为
2、由于不要求最小的k个数按序输出,参考快排中partition函数思想:每轮排序之后枢轴左边数字都比它小,右边数字都比它大。因此如果排序之后枢轴刚好处于数组的第k个位置,那么此时数组的前k个数字也即是最小的k个数
这种思想经证明是O(n)的时间复杂度(有兴趣的可以参考《算法导论》)
代码实现如下:
# -*- coding:utf-8 -*-
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
n = len(tinput)
if k <= 0 or k > n:
return list()
start = 0
end = n - 1
mid = self.partition(tinput, start, end)
while k - 1 != mid:
if k - 1 > mid:
start = mid + 1
mid = self.partition(tinput, start, end)
elif k - 1 < mid:
end = mid - 1
mid = self.partition(tinput, start, end)
res = tinput[:mid+1]
# res.sort()
return res
def partition(self, numbers, low, high):
key = numbers[low]
while low < high:
while low < high and numbers[high] >= key:
high -= 1
numbers[low] = numbers[high]
while low < high and numbers[low] <= key:
low += 1
numbers[high] = numbers[low]
numbers[low] = key
return low
3、一个很常见的例子,假设我们要选5个最矮的同学参加比赛,候选人选好之后又跑来一个新同学参加评选。这个时候新同学只需要PK候选人中最高的,如果他比最高的还高,就被淘汰,如果比最高的矮,他就可以替代这个最高的。按照这个思想,无论再来多少新同学都可以很快速的选择。
以这个思想为基础,为了最快的找到候选人中最高的,以身高为基准建立大顶堆,每次新人与堆顶PK,PK结束之后维护候选人重新为大顶堆。
这种思想将时间复杂度降到以k为基数,每次调整堆的时间复杂度都是,那么整体时间复杂度为。
实现代码如下:
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
n = len(tinput)
if k <= 0 or k > n:
return list()
# 建立大顶堆
for i in range(int(k / 2) - 1, -1, -1):
self.heapAjust(tinput, i, k - 1)
for i in range(k, n):
if tinput[i] < tinput[0]:
tinput[0], tinput[i] = tinput[i], tinput[0]
# 调整前k个数
self.heapAjust(tinput, 0, k - 1)
print(tinput[:k])
def heapAjust(self, nums, start, end):
temp = nums[start]
# 记录较大的那个孩子下标
child = 2 * start + 1
while child <= end:
# 比较左右孩子,记录较大的那个
if child + 1 <= end and nums[child] < nums[child + 1]:
# 如果右孩子比较大,下标往右移
child += 1
# 如果根已经比左右孩子都大了,直接退出
if temp >= nums[child]:
break
# 如果根小于某个孩子,将较大值提到根位置
nums[start] = nums[child]
# nums[start], nums[child] = nums[child], nums[start]
# 接着比较被降下去是否符合要求,此时的根下标为原来被换上去的那个孩子下标
start = child
# 孩子下标也要下降一层
child = child * 2 + 1
# 最后将一开始的根值放入合适的位置(如果前面是交换,这句就不要)
nums[start] = temp
值得一提的是,最后一种方法将全部排序降为部分排序,这在海量数据中查找topK中是很有用的,当数据是增量式,或者无法全部加载进内存中时,只开辟一小部分空间存储k个数字还是可以实现的。