窥探算法之美妙——寻找数组中最小的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个数.例如 ...
随机推荐
- ASP.NET MVC 在WebService中Token的使用方法
最近发现公司接口的验密方式很简单,就是简单的用户名密码校验.客户方面的负责人说要修改一下,所以想起了微信的验证密码的方式故写了这个Demo以供大家学习参考: 接口:WebService 方式:Toke ...
- index merge的一次优化
手机微博4040端口SQL优化 现象 某端口常态化延迟,通过使用pt-query-digest发现主要由于一条count(*)语句引发,具体如下: # .5s .58M rss, .84M vsz # ...
- 第三篇 :微信公众平台开发实战Java版之请求消息,响应消息以及事件消息类的封装
微信服务器和第三方服务器之间究竟是通过什么方式进行对话的? 下面,我们先看下图: 其实我们可以简单的理解: (1)首先,用户向微信服务器发送消息: (2)微信服务器接收到用户的消息处理之后,通过开发者 ...
- [转]Android输出Log到文件
前言:开发中遇到mx4这款机型Eclipse联调不上,logcat看不了,需要输出生成文件查看调试信息.网上搜了下,功能很完善了.startService和过滤输出信息需要自己添加设置,另外注意添加权 ...
- CANopen DS301协议中文翻译V03版
V0.1版PDF格式供下载参考,只是全面框架翻译,会有大量错误和不确定的地方,希望读者积极参与校对,提供修改意见,完善译文.下载 V0.2版校对提前完成,下载地址 V0.3版使用GitBook编辑(h ...
- java基础疑难点总结之成员变量的继承,方法重载与重写的区别,多态与动态绑定
1.成员变量的继承 1.1要点 子类用extends关键字继承父类.子类中可以提供新的方法覆盖父类中的方法.子类中的方法不能直接访问父类中的私有域,子类可以用super关键字调用父类中的方法.在子类中 ...
- Hello world S.B.S.
#include <iostream> #include <conio.h> #include<cstdio> #include<cstring> #i ...
- POJ 3304 Segments --枚举,几何
题意: 给n条线段,问有没有一条直线,是每条线段到这条直线上的投影有一个公共点. 解法: 有公共点说明有一条这条直线的垂线过所有线段,要找一条直线过所有线段,等价于从所有线段中任选两端点形成的直线存在 ...
- Android组件---四大布局的属性详解
[声明] 欢迎转载,但请保留文章原始出处→_→ 文章来源:http://www.cnblogs.com/smyhvae/p/4372222.html Android常见布局有下面几种: LinearL ...
- Unity 2D Touch Movement
Demo试玩(Kongregate既然也有广告时间了 --!)http://www.kongregate.com/games/zhaoqingqing/2d-touch-movement 操作步骤 1 ...