top k高频面试题+赛马问题

前几天面试被问到这两个问题,真尴尬,都没怎么答对,今天就让我们来分析一下这个问题

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



 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值