我正在尝试找到5个排序数组的中位数的解决方案。这是一个面试问题。
我能想到的解决方案是合并5个数组,然后找到中位数[O(l + m + n + o + p)]。
我知道对于2个相同大小的排序数组,我们可以在log(2n)中完成。 [通过比较两个阵列的中位数,然后丢弃每个阵列的一半并重复该过程]。 ..查找中位数可以是排序数组中的常量时间..所以我认为这不是log(n)? ..这个时间复杂度是多少?
1] 5个阵列是否有类似的解决方案。如果阵列大小相同,那么有更好的解决方案吗?
2]我假设因为要求5,N排序数组会有一些解决方案吗?
感谢您的任何指示。
我向面试官回答了一些澄清/问题:
是相同长度的阵列
=>没有
我猜数组的值会有重叠
=>是
作为练习,我认为2个阵列的逻辑不会延伸。这是一个尝试:
将2个数组的上述逻辑应用于3个数组:
[3,7,9] [4,8,15] [2,3,9] ......中位数7,8,3
抛出元素[3,7,9] [4,8] [3,9] ..中位数7,6,6
抛出元素[3,7] [8] [9] ..中文5,8,9 ...
throw elements [7] [8] [9] .. median = 8 ...这似乎不正确吗?
排序元素的合并=> [2,3,4,7,8,9,15] =>预期中位数= 7
答案 0 :(得分:26)
(这是对两个数组的想法的概括。)
如果你从五个阵列中的五个中位数开始,显然总体中位数必须介于五个中位数的最小值和最大值之间。
证明是这样的:如果a是中位数的最小值,而b是中位数的最大值,那么每个数组的元素少于一半,而不到一半的元素大于b 。结果如下。
所以在包含a的数组中,丢弃小于a的数字;在包含b的数组中,丢弃大于b的数字......但只丢弃两个数组中相同数量的元素。
也就是说,如果a是从其数组开始的j个元素,并且b是从其数组末尾开始的k个元素,则会丢弃数组中的第一个min(j,k)元素和最后一个min( j,k)来自b阵列的元素。
迭代直到你总共减少1或2个元素。
这些操作中的每一个(即,找到排序数组的中值并从数组的开头或结尾丢弃k个元素)是恒定时间。所以每次迭代都是恒定的时间。
每次迭代都会丢弃(超过)至少一个数组中的一半元素,并且您只能为五个数组中的每一个执行log(n)次...所以整个算法是log(n)。
[更新]
正如Himadri Choudhury在评论中指出的那样,我的解决方案是不完整的;有很多细节和角落需要担心。所以,要把事情搞得一团糟......
对于五个数组R中的每一个,将其“下中位数”定义为R [n / 2-1],将其“上中位数”定义为R [n / 2],其中n是数组中元素的数量(并且数组从0开始索引,并且除以2舍入)。
让“a”成为下中位数中最小的,而“b”是上位中位数中最大的。如果有多个具有最小中位数的数组和/或具有最大上中位数的多个数组,请从不同的数组中选择a和b(这是其中一个极端情况)。
现在,借用Himadri的建议:从其数组中删除所有并包括 a的元素,将所有元素从中删除并包括 b,注意从两个数组中删除相同数量的元素。注意a和b可以在同一个数组中;但如果是这样,它们就不能具有相同的值,因为否则我们就可以从不同的数组中选择其中一个。所以如果这一步结束了从同一个数组的开头和结尾抛弃元素,那就没关系了。
只要您有三个或更多阵列就可以迭代。但是,一旦你只有一两个阵列,你必须改变你的战略,而不是包容;你只删除但不包括 a和但不包括 b。只要剩下的一个或两个数组都至少有三个元素(保证你取得进展),就像这样继续。
最后,你将减少到几种情况,其中最棘手的是剩下两个数组,其中一个有一个或两个元素。现在,如果我问你:“给定一个排序的数组加上一个或两个额外的元素,找到所有元素的中位数”,我认为你可以在恒定的时间内做到这一点。 (同样,有很多细节可以解决,但基本的想法是,在数组中添加一个或两个元素并不会“非常推动中位数”。)
答案 1 :(得分:1)
应该非常直接地将相同的想法应用于5个阵列。
首先,将问题转换为更一般的问题。在N个排序数组中查找Kth元素
使用二分搜索在每个已排序的数组中查找(K / N)个元素,例如K1,K2 ... KN
Kmin = min(K1 ... KN),Kmax = max(K1 ... KN)
丢弃所有小于Kmin或大于Kmax的元素,比如X元素已被丢弃。
现在通过在排序数组中使用剩余元素查找(K - X)元素来重复此过程
答案 2 :(得分:1)
您无需对5个阵列进行完全合并。你可以进行合并排序,直到你有(l + n + o + p + q)/ 2个元素,然后你就得到了中值。
答案 3 :(得分:0)
可以通过 binary search
找到已排序列表中的第 k 个元素。
from bisect import bisect_left
from bisect import bisect_right
def kthOfPiles(givenPiles, k, count):
'''
Perform binary search for kth element in multiple sorted list
parameters
==========
givenPiles are list of sorted list
count is the total number of
k is the target index in range [0..count-1]
'''
begins = [0 for pile in givenPiles]
ends = [len(pile) for pile in givenPiles]
#print('finding k=', k, 'count=', count)
for pileidx,pivotpile in enumerate(givenPiles):
while begins[pileidx] < ends[pileidx]:
mid = (begins[pileidx]+ends[pileidx])>>1
midval = pivotpile[mid]
smaller_count = 0
smaller_right_count = 0
for pile in givenPiles:
smaller_count += bisect_left(pile,midval)
smaller_right_count += bisect_right(pile,midval)
#print('check midval', midval,smaller_count,k,smaller_right_count)
if smaller_count <= k and k < smaller_right_count:
return midval
elif smaller_count > k:
ends[pileidx] = mid
else:
begins[pileidx] = mid+1
return -1
def medianOfPiles(givenPiles,count=None):
'''
Find statistical median
Parameters:
givenPiles are list of sorted list
'''
if not givenPiles:
return -1 # cannot find median
if count is None:
count = 0
for pile in givenPiles:
count += len(pile)
# get mid floor
target_mid = count >> 1
midval = kthOfPiles(givenPiles, target_mid, count)
if 0 == (count&1):
midval += kthOfPiles(givenPiles, target_mid-1, count)
midval /= 2
return '%.1f' % round(midval,1)
上面的代码也给出了正确的统计中位数。
将上面的二分搜索与 patience-sort
相结合,提供了一种有价值的技术。
值得一提的是用于选择枢轴的median of median
算法。它给出了近似值。我想这与我们在这里问的不同。