前几天面试被问到这两个问题,真尴尬,都没怎么答对,今天就让我们来分析一下这个问题
top k大和top k高频问题,因为在求前k小的数时可能会有重复,因此我们可以利用hash表或字典来去重。同样适用与Top K 问题
接下来要从海量数据中找最小的K个数,建议分为3个方面来回答问题
1)数据无法全部加载到内存中 2)数据可以全部加载 3)分布式数据处理
对于数据无法全部加载时,我们可以维护最大堆来处理问题,注意注意!前k小用最大堆,前k大用最小堆,为什么呢?因为最大堆是左右子树都小于root值,因此每次有比堆顶元素小的值加加入堆中,这样成功维护了前k个数,是不是很强,我也觉得!
然后对于最小堆、最大堆C++中可以用优先队列实现,python里可以用heap函数实现,同时count函数可以统计频数
对于时间复杂度,建堆时间复杂度是O(logK),一共需要遍历n个数,所以时间复杂度是O(NlogK)
这里推荐一下:https://blog.csdn.net/v_july_v/article/details/7382693
class Solution:
def smallestK(self, arr: List[int], k: int) -> List[int]:
if k>len(arr) or k==0:
return []
heap=[]
for i in arr[:k]:
heapq.heappush(heap,-i)
for i in arr[k:]:
if i <-heap[0]:
heapq.heappop(heap)
heapq.heappush(heap,-i)
res = []
for i in range(k):
res.append(-heapq.heappop(heap))
return res[::-1]
#利用heapq.heappush()、heapq.heappop()函数进行最小堆的构建,但是这是求前k大数
#最大堆的时候有技巧。push(-i),-pop(i)实现最大堆,逻辑是相通的
前k个高频词汇:
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
count = collections.Counter(nums)
return heapq.nlargest(k, count.keys(), key=count.get)
###哈希表 + 最小堆维护出现高频k个元素
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
from collections import Counter
from heapq import *
# time: O(nlogk)
# space: O(n)
dic = Counter(nums) # O(n)
heap = []
heapify(heap)
for key, val in dic.items(): # O(nlogk)
if len(heap) < k:
heappush(heap, (val, key))
else:
if heap[0][0] < val:
heappush(heap, (val, key))
heappop(heap)
# print heap
res = []
while heap: # O(klogk)
top = heappop(heap)
res.append(top[1])
return res
无序数组的中位数:
最简单的方法是先将数组排序,然后找中位数。但此种方法肯定不是最优的。一个比较好的做法是利用小顶堆。思路如下:
1.取前len(nums)//2个元素建立小顶堆。可以知道堆顶元素是前len(nums)/2个元素中最小的。
2.从第len(nums)//2+1个元素开始,依次将其与堆顶元素比较。若比对顶元素大,则替换之,并调整堆。
3.数组剩下的所有元素比较完后,可以输出中位数。数组长度为奇数时,输出堆顶元素即可。数组长度为偶数时,输出堆顶元素与它的孩子结点中较小的那个的均值。
from heapq import *
import heapq
def find(nums):
heap=[]
n = len(nums)
for i in range(n//2+1):
heappush(heap,nums[i])
#print(heap)
for j in range(n//2+1,n):
print(j)
if nums[j] > heap[0]:
heappop(heap)
heappush(heap, nums[j])
#print(heap)
#print(heap)
if n%2==1:
return heap[0]
else:
res = heapq.nsmallest(2,heap)
return float(res[0]+res[1])/2
print(find([1,2,3,6,4,5]))
2)当数据可以全部加载时
可以将数据全部排序,但这样时间复杂度是O(N平方),比较巧妙的是利用快排的partion函数,哨兵选择在中间,这样当中间位置等于K时,表示前k个数即为前k小,这个时间复杂度是O(N),因为是n/2+4/n+.....<2n,所以时间复杂度是线性的
3)分布式数据处理
此时体现分治和归并的思想,可以将所有数都利用散列函数分到m个机器上,散列函数尽量保证数值不要有重复,而后分布式求前k小,再归并最终求出前k小。完卒,hhh
关于赛马问题,真是个头疼的问题呦,不过又能怎么样呢、
1、25匹马,有一条只能5匹马比赛的赛道,我们无法计时,只能看到马的排名,如何用最短的次数找出跑的最快的5匹马?
这道题目的话最好的情况是7次,最坏的情况是10次。我们首先建立一个表格,先把25匹马分为如下的五组:
每组进行比赛,假设第一组快慢顺序为A1、A2、A3、A4和A5,第二组依次类推。那么各组的第一分别是A1、B1、C1、D1、E1。
在最好的情况下,先让A1、B1、C1、D1、E1比赛,得到第一名,假设A1是第一名,并且顺序是A1 > B1 > C1 > D1 > E1;然后让A2、A3、A4、A5和B1进行比赛,若B1最慢,那么前五名就是A1-A5,共需比赛7次。那么在最坏的情况下,每次新加入一个候补,得到一个新的名次的马,此时共需要10次比赛。
这个题更加常考的是问如何用最短的次数找出最快的3匹马,这个题和找出5匹马还不太一样。如果找出3匹马,只需要比赛7次即可,前六次假设和上面的过程一样,A1是最快的马,剩下的名次是B1 > C1 > D1 > E1。此时并不是让A2、B1、C1、D1、E1进行比赛,先仔细分析一下,第二名一定出现在B1 和 A2之中,若B1 > A2,那么第三名出现在A2 、B2、C1之中,若A2 > B1,那么第三名出现在A3 、B1之中。因此,第七场比赛只需要让A2、A3、B1、B2、C1五匹马比赛,得到前两名即可。因此只需要7场比赛就可以得到跑的最快的3匹马。hhh