原文发表在个人博客主页,转载请注明出处python
不管是小算法或者大系统,堆一直是某种场景下程序员比较亲睐的数据结构,而在python中,因为数据结构的极其灵活性,list,tuple, dict在不少状况下能够模拟其余数据结构,Queue库提供了栈和队列,甚至优先队列(和最小堆相似),heapq提供了最小堆,树,链表的指针在python中能够看成最普通的变量,因此python大法好。。。使用python确实能够把程序员从复杂的数据结构中解放开来,重点关注算法。好了言归正传。程序员
前几天看到了一个很经典的算法题目:输入n个整数,找出其中最小的k个数算法
这道题目自己不是很难,而这篇博客更加侧重的是python中的最大堆的使用以及这道题目的解法汇总。数组
这个思路应该是最简单的,将整个数组排序,而后取出前k个数据就能够了,这个算法的时间复杂度为nlog(n),这里展现快速排序。代码以下:数据结构
def partition(alist, start, end): if end <= start: return base = alist[start] index1, index2 = start, end while start < end: while start < end and alist[end] >= base: end -= 1 alist[start] = alist[end] while start < end and alist[start] <= base: start += 1 alist[end] = alist[start] alist[start] = base partition(alist, index1, start - 1) partition(alist, start + 1, index2) def find_least_k_nums(alist, k): length = len(alist) if not alist or k <=0 or k > length: return None start = 0 end = length - 1 partition(alist, start, end) return alist[:k] if __name__ == "__main__": l = [1, 9, 2, 4, 7, 6, 3] min_k = find_least_k_nums(l, 7) print min_k
这种解法是在第一种解法上面的一种改进,快速排序的思想你们都已经知道,如今咱们只须要最小的k个数,因此若是咱们在某次快速排序中,选择的基准树的大小恰好是整个数组的第k小的数据,那么在此次排序完成以后,这个基准数以前的数据就是咱们须要的(尽管他们并非有序的),这个方法一样改变了数组,可是能够将时间复杂度压缩到O(n),话很少说,直接上代码:app
def partition(alist, start, end): if end <= start: return base = alist[start] index1, index2 = start, end while start < end: while start < end and alist[end] >= base: end -= 1 alist[start] = alist[end] while start < end and alist[start] <= base: start += 1 alist[end] = alist[start] alist[start] = base return start def find_least_k_nums(alist, k): length = len(alist) #if length == k: # return alist if not alist or k <=0 or k > length: return start = 0 end = length - 1 index = partition(alist, start, end) while index != k: if index > k: index = partition(alist, start, index - 1) elif index < k: index = partition(alist, index + 1, end) return alist[:k] if __name__ == "__main__": l = [1, 9, 2, 4, 7, 6, 3] min_k = find_least_k_nums(l, 6) print min_k
上面方法虽然要改变数组的结构,在不要求数字顺序的状况下使用能够得到很好的时间复杂度,可是假如数字很是的多,一次性将其载入内存变得不可能或者内存消耗过大,那上面的方法就再也不可行,咱们能够建立一个大小为K的数据容器来存储最小的K个数,而后遍历整个数组,将每一个数字和容器中的最大数进行比较,若是这个数大于容器中的最大值,则继续遍历,不然用这个数字替换掉容器中的最大值。这个方法的理解也十分简单,至于容器的选择,不少人第一反应即是最大堆,可是python中最大堆如何实现呢?咱们能够借助实现了最小堆的heapq库,由于在一个数组中,每一个数取反,则最大数变成了最小数,整个数字的顺序发生了变化,因此能够给数组的每一个数字取反,而后借助最小堆,最后返回结果的时候再取反就能够了,代码以下:大数据
import heapq def get_least_numbers_big_data(self, alist, k): max_heap = [] length = len(alist) if not alist or k <= 0 or k > length: return k = k - 1 for ele in alist: ele = -ele if len(max_heap) <= k: heapq.heappush(max_heap, ele) else: heapq.heappushpop(max_heap, ele) return map(lambda x:-x, max_heap) if __name__ == "__main__": l = [1, 9, 2, 4, 7, 6, 3] min_k = get_least_numbers_big_data(l, 3)
前面两种方法在数据量较小的时候若是容许改变数组结构可使用,可是在大数据场景中,同时不改变数组结构,可使用第三种方法。指针