我最近接受了一家社交媒体公司的采访,有人问我以下问题。
有k个长度为m的未排序数组。我们的目标是在给定a<b<m的情况下,以一种高效且内存保守的方式在k个数组中找到第a到第b个最小元素。在接下来的问题中,mysql数据库中的“未排序数组”被改为跨不同表的列,可以使用什么样的高效数据结构,相应的检索算法是什么。
我提出了两种可能的解决方案:
第一:暴力:
首先使用quickselect查找每个数组的第b个最小元素。
然后找到小于每个数组第b个元素的元素,并将它们存储到大小为kb的b树c中。
然后找出c中的第a到第b个最小元素。
对于使用quickselect查找第b个最小元素的第一步,平均时间总共是从o(km)到o(kmlog(m))。第二步时间复杂度为o(km)。最后一步是查找c中第a个和第b个最小元素之间的元素,取o((b-a)log(kb))。所以total在时间上需要o(km)到o(kmlog(m))+o((b-a)log(kb)),在空间上需要o(kb)。
第二:递归地弹出最小的元素
对于每个循环,执行
找到所有k个数组的最小元素,存储在b树c中
在c中找到最小的元素,从c中弹出这个元素,然后从数组中得到它。
重复上述步骤,直到弹出a-1数字,然后转到4
存储从a到b的值,同时重复1到2
因此计算复杂度为o(klog(k))+o(blog(k)),空间复杂度为o(max(k,b-a))。这似乎是最小的空间复杂性。
有什么更有效的方法可以做到这一点?特别是quickselect的最坏情况是o(n^2),它看起来太大了,对于b=m/2,在空间上的中位数o(kb)或时间上的中位数o(blog(k))被认为太大了。对于mysql数据库,我建议在解决方案1中使用b-tree,它可以在空间和时间上都有o(kb)的情况下提供快速的秩选择,在数据库中有k个查询。而在解决方案2中,据说b查询到mysql数据库太大,b树插入是o(log(m)),其中m可能非常大。
1条答案
按热度按时间jvlzgdj91#
一种简单的方法是创建一个大小为b的max堆。然后运行以下代码:
这里的想法是用前b项填充max堆。然后,对于每一个其他项,如果它小于堆上最大的项,则用新项移除堆上最大的项。
处理完所有km项后,最小的b项在堆上,因为它是max堆,所以首先弹出的b-a项将是所有k数组中的ath到bth项。
最坏的情况是使用o(b)额外内存,第一个循环为o(kmlogb),第二个循环为o(blogb)。
如果允许销毁源数组,可以编写一个自定义的quickselect,将k个数组作为单个数组进行索引。也就是o(km),使用o(k)额外内存作为间接索引。缺点是索引代码会稍微慢一些。当然,项目会在数组之间移动。您可能需要o(b)额外的内存作为返回值。渐近地,它比我最初的选择更有效。它能否跑得更快完全是另一个问题。
另一种可能性。在每个k数组上运行buildheap方法。也就是0(公里)。然后进行合并以选择前b项。合并需要:
o(logm)从源数组中删除每个项
o(logb)将每个项添加到合并堆
o(logb)从合并堆中删除每个项
第二步是o(b*(logm+logb+logb))。
这样就得到了o(km+b*(logm+logb+logb)),您将使用o(b)额外的内存。这是否会比最初的建议更快是值得怀疑的。这取决于b和m之间的关系。b值越大,速度越慢。而且代码编写起来要复杂得多。