原文发表在我的博客主页,转载请注明出处

前言

不论是小算法或者大系统,堆一直是某种场景下程序员比较亲睐的数据结构,而在python中,由于数据结构的极其灵活性,list,tuple, dict在很多情况下可以模拟其他数据结构,Queue库提供了栈和队列,甚至优先队列(和最小堆类似),heapq提供了最小堆,树,链表的指针在python中可以当作最普通的变量,所以python大法好。。。使用python确实可以把程序员从复杂的数据结构中解放开来,重点关注算法。好了言归正传。

题目

前几天看到了一个很经典的算法题目:输入n个整数,找出其中最小的k个数

解决办法

这道题目本身不是很难,而这篇博客更加侧重的是python中的最大堆的使用以及这道题目的解法汇总。

一. 排序

这个思路应该是最简单的,将整个数组排序,然后取出前k个数据就可以了,这个算法的时间复杂度为nlog(n),这里展示快速排序。代码如下:

  1. def partition(alist, start, end):
  2. if end <= start:
  3. return
  4. base = alist[start]
  5. index1, index2 = start, end
  6. while start < end:
  7. while start < end and alist[end] >= base:
  8. end -= 1
  9. alist[start] = alist[end]
  10. while start < end and alist[start] <= base:
  11. start += 1
  12. alist[end] = alist[start]
  13. alist[start] = base
  14. partition(alist, index1, start - 1)
  15. partition(alist, start + 1, index2)
  16. def find_least_k_nums(alist, k):
  17. length = len(alist)
  18. if not alist or k <=0 or k > length:
  19. return None
  20. start = 0
  21. end = length - 1
  22. partition(alist, start, end)
  23. return alist[:k]
  24. if __name__ == "__main__":
  25. l = [1, 9, 2, 4, 7, 6, 3]
  26. min_k = find_least_k_nums(l, 7)
  27. print min_k

二. 快速排序的思想

这种解法是在第一种解法上面的一种改进,快速排序的思想大家都已经知道,现在我们只需要最小的k个数,所以如果我们在某次快速排序中,选择的基准树的大小刚好是整个数组的第k小的数据,那么在这次排序完成之后,这个基准数之前的数据就是我们需要的(尽管他们并不是有序的),这个方法同样改变了数组,但是可以将时间复杂度压缩到O(n),话不多说,直接上代码:

  1. def partition(alist, start, end):
  2. if end <= start:
  3. return
  4. base = alist[start]
  5. index1, index2 = start, end
  6. while start < end:
  7. while start < end and alist[end] >= base:
  8. end -= 1
  9. alist[start] = alist[end]
  10. while start < end and alist[start] <= base:
  11. start += 1
  12. alist[end] = alist[start]
  13. alist[start] = base
  14. return start
  15. def find_least_k_nums(alist, k):
  16. length = len(alist)
  17. #if length == k:
  18. # return alist
  19. if not alist or k <=0 or k > length:
  20. return
  21. start = 0
  22. end = length - 1
  23. index = partition(alist, start, end)
  24. while index != k:
  25. if index > k:
  26. index = partition(alist, start, index - 1)
  27. elif index < k:
  28. index = partition(alist, index + 1, end)
  29. return alist[:k]
  30. if __name__ == "__main__":
  31. l = [1, 9, 2, 4, 7, 6, 3]
  32. min_k = find_least_k_nums(l, 6)
  33. print min_k

三. 最大堆

上面方法虽然要改变数组的结构,在不要求数字顺序的情况下使用可以获得很好的时间复杂度,但是假如数字非常的多,一次性将其载入内存变得不可能或者内存消耗过大,那上面的方法就不再可行,我们可以创建一个大小为K的数据容器来存储最小的K个数,然后遍历整个数组,将每个数字和容器中的最大数进行比较,如果这个数大于容器中的最大值,则继续遍历,否则用这个数字替换掉容器中的最大值。这个方法的理解也十分简单,至于容器的选择,很多人第一反应便是最大堆,但是python中最大堆如何实现呢?我们可以借助实现了最小堆的heapq库,因为在一个数组中,每个数取反,则最大数变成了最小数,整个数字的顺序发生了变化,所以可以给数组的每个数字取反,然后借助最小堆,最后返回结果的时候再取反就可以了,代码如下:

  1. import heapq
  2. def get_least_numbers_big_data(self, alist, k):
  3. max_heap = []
  4. length = len(alist)
  5. if not alist or k <= 0 or k > length:
  6. return
  7. k = k - 1
  8. for ele in alist:
  9. ele = -ele
  10. if len(max_heap) <= k:
  11. heapq.heappush(max_heap, ele)
  12. else:
  13. heapq.heappushpop(max_heap, ele)
  14. return map(lambda x:-x, max_heap)
  15. if __name__ == "__main__":
  16. l = [1, 9, 2, 4, 7, 6, 3]
  17. min_k = get_least_numbers_big_data(l, 3)

总结

前面两种方法在数据量较小的时候如果允许改变数组结构可以使用,但是在大数据场景中,同时不改变数组结构,可以使用第三种方法。

窥探算法之美妙——寻找数组中最小的K个数&python中巧用最大堆的更多相关文章

  1. [算法]找到无序数组中最小的K个数

    题目: 给定一个无序的整型数组arr,找到其中最小的k个数. 方法一: 将数组排序,排序后的数组的前k个数就是最小的k个数. 时间复杂度:O(nlogn) 方法二: 时间复杂度:O(nlogk) 维护 ...

  2. 【算法】数组与矩阵问题——找到无序数组中最小的k个数

    /** * 找到无序数组中最小的k个数 时间复杂度O(Nlogk) * 过程: * 1.一直维护一个有k个数的大根堆,这个堆代表目前选出来的k个最小的数 * 在堆里的k个元素中堆顶的元素是最小的k个数 ...

  3. 求一个数组中最小的K个数

    方法1:先对数组进行排序,然后遍历前K个数,此时时间复杂度为O(nlgn); 方法2:维护一个容量为K的最大堆(<算法导论>第6章),然后从第K+1个元素开始遍历,和堆中的最大元素比较,如 ...

  4. 小米笔试题:无序数组中最小的k个数

    题目描述 链接:https://www.nowcoder.com/questionTerminal/ec2575fb877d41c9a33d9bab2694ba47?source=relative 来 ...

  5. 《程序员代码面试指南》第八章 数组和矩阵问题 找到无序数组中最小的k 个数

    题目 找到无序数组中最小的k 个数 java代码 package com.lizhouwei.chapter8; /** * @Description: 找到无序数组中最小的k 个数 * @Autho ...

  6. 求给定数据中最小的K个数

    public class MinHeap { /* * * Top K个问题,求给定数据中最小的K个数 * * 最小堆解决:堆顶元素为堆中最大元素 * * * */ private int MAX_D ...

  7. Java找N个数中最小的K个数,PriorityQueue和Arrays.sort()两种实现方法

    最近看到了 java.util.PriorityQueue.刚看到还没什么感觉,今天突然发现他可以用来找N个数中最小的K个数. 假设有如下 10 个整数. 5 2 0 1 4 8 6 9 7 3 怎么 ...

  8. 求数组中最小的k个数

    题目:输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. package test; import java.util.Arra ...

  9. [剑指offer]数组中最小的K个数,C++实现

    原创博文,转载请注明出处! http://github.com/wanglei5205 http://cnblogs.com/wanglei5205 # 题目 输入n个整数,找出其中最小的K个数.例如 ...

随机推荐

  1. JavaScript Patterns 3.4 Array Literal

    Array Literal Syntax To avoid potential errors when creating dynamic arrays at runtime, it's much sa ...

  2. .net 中使用配置文件需注意引用dll文件

    需要用到sqlhelper和配置文件时发现加了using System.Configuration;还是不能用ConfigurationManager. 查了几遍msdn未果,直接百度才发现还需要引用 ...

  3. 【nginx】关于gzip压缩

    有这么一段配置文件 gzip on # 默认值: gzip off # 开启或者关闭gzip模块 gzip_static off; # nginx对于静态文件的处理模块 # 该模块可以读取预先压缩的g ...

  4. 使用Spring Security Oauth2完成RESTful服务password认证的过程

            摘要:Spring Security与Oauth2整合步骤中详细描述了使用过程,但它对于入门者有些重量级,比如将用户信息.ClientDetails.token存入数据库而非内存.配置 ...

  5. JS高级程序设计2nd部分知识要点5

    JS Regexp 字面量模式 用\反斜杠转义 构造函数中的字符串 也用\转义正则也用\ RegExp实例属性 global -布尔值  /g ignoreCase -布尔值 /i lastIndex ...

  6. android怎么连接sqlite数据库?

    SQLite数据库首先先建立SQLiteOpenHelper()的子类实现SQLiteOpenHelper中的OnCreate()方法和构造方法. this class takes care of o ...

  7. MFC 窗口分割

    动态分割窗口: BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { , , CSize(, ...

  8. Button未设type属性时在非IE6/7中具有submit特性

    代码如下 <!DOCTYPE html> <html> <head> <title>Button在Form中具有submit的特性</title& ...

  9. Android 渗透测试学习手册 翻译完成!

    原书:Learning Pentesting for Android Devices 译者:飞龙 在线阅读 PDF格式 EPUB格式 MOBI格式 代码仓库 赞助我 协议 CC BY-NC-SA 4. ...

  10. 解决在iOS8环境下,当用户关闭定位服务总开关时,无法将APP定位子选项加入定位权限列表的问题

    关键点:- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizati ...