窥探算法之美妙——寻找数组中最小的K个数&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),话不多说,直接上代码:
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)
总结
前面两种方法在数据量较小的时候如果允许改变数组结构可以使用,但是在大数据场景中,同时不改变数组结构,可以使用第三种方法。
窥探算法之美妙——寻找数组中最小的K个数&python中巧用最大堆的更多相关文章
- [算法]找到无序数组中最小的K个数
题目: 给定一个无序的整型数组arr,找到其中最小的k个数. 方法一: 将数组排序,排序后的数组的前k个数就是最小的k个数. 时间复杂度:O(nlogn) 方法二: 时间复杂度:O(nlogk) 维护 ...
- 【算法】数组与矩阵问题——找到无序数组中最小的k个数
/** * 找到无序数组中最小的k个数 时间复杂度O(Nlogk) * 过程: * 1.一直维护一个有k个数的大根堆,这个堆代表目前选出来的k个最小的数 * 在堆里的k个元素中堆顶的元素是最小的k个数 ...
- 求一个数组中最小的K个数
方法1:先对数组进行排序,然后遍历前K个数,此时时间复杂度为O(nlgn); 方法2:维护一个容量为K的最大堆(<算法导论>第6章),然后从第K+1个元素开始遍历,和堆中的最大元素比较,如 ...
- 小米笔试题:无序数组中最小的k个数
题目描述 链接:https://www.nowcoder.com/questionTerminal/ec2575fb877d41c9a33d9bab2694ba47?source=relative 来 ...
- 《程序员代码面试指南》第八章 数组和矩阵问题 找到无序数组中最小的k 个数
题目 找到无序数组中最小的k 个数 java代码 package com.lizhouwei.chapter8; /** * @Description: 找到无序数组中最小的k 个数 * @Autho ...
- 求给定数据中最小的K个数
public class MinHeap { /* * * Top K个问题,求给定数据中最小的K个数 * * 最小堆解决:堆顶元素为堆中最大元素 * * * */ private int MAX_D ...
- Java找N个数中最小的K个数,PriorityQueue和Arrays.sort()两种实现方法
最近看到了 java.util.PriorityQueue.刚看到还没什么感觉,今天突然发现他可以用来找N个数中最小的K个数. 假设有如下 10 个整数. 5 2 0 1 4 8 6 9 7 3 怎么 ...
- 求数组中最小的k个数
题目:输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. package test; import java.util.Arra ...
- [剑指offer]数组中最小的K个数,C++实现
原创博文,转载请注明出处! http://github.com/wanglei5205 http://cnblogs.com/wanglei5205 # 题目 输入n个整数,找出其中最小的K个数.例如 ...
随机推荐
- JavaScript Patterns 3.4 Array Literal
Array Literal Syntax To avoid potential errors when creating dynamic arrays at runtime, it's much sa ...
- .net 中使用配置文件需注意引用dll文件
需要用到sqlhelper和配置文件时发现加了using System.Configuration;还是不能用ConfigurationManager. 查了几遍msdn未果,直接百度才发现还需要引用 ...
- 【nginx】关于gzip压缩
有这么一段配置文件 gzip on # 默认值: gzip off # 开启或者关闭gzip模块 gzip_static off; # nginx对于静态文件的处理模块 # 该模块可以读取预先压缩的g ...
- 使用Spring Security Oauth2完成RESTful服务password认证的过程
摘要:Spring Security与Oauth2整合步骤中详细描述了使用过程,但它对于入门者有些重量级,比如将用户信息.ClientDetails.token存入数据库而非内存.配置 ...
- JS高级程序设计2nd部分知识要点5
JS Regexp 字面量模式 用\反斜杠转义 构造函数中的字符串 也用\转义正则也用\ RegExp实例属性 global -布尔值 /g ignoreCase -布尔值 /i lastIndex ...
- android怎么连接sqlite数据库?
SQLite数据库首先先建立SQLiteOpenHelper()的子类实现SQLiteOpenHelper中的OnCreate()方法和构造方法. this class takes care of o ...
- MFC 窗口分割
动态分割窗口: BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { , , CSize(, ...
- Button未设type属性时在非IE6/7中具有submit特性
代码如下 <!DOCTYPE html> <html> <head> <title>Button在Form中具有submit的特性</title& ...
- Android 渗透测试学习手册 翻译完成!
原书:Learning Pentesting for Android Devices 译者:飞龙 在线阅读 PDF格式 EPUB格式 MOBI格式 代码仓库 赞助我 协议 CC BY-NC-SA 4. ...
- 解决在iOS8环境下,当用户关闭定位服务总开关时,无法将APP定位子选项加入定位权限列表的问题
关键点:- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizati ...