给出了两个长度为n的排序数组,问题是在O(n)时间内找到它们的和数组的中值,它包含数组A的每个元素和数组B的每个元素之间的所有可能的成对和 .
例如:令A [2,4,6]和B [1,3,5]为两个给定的数组 . sum数组是 [2+1,2+3,2+5,4+1,4+3,4+5,6+1,6+3,6+5]
. 在O(n)中找到该数组的中位数 .
在O(n ^ 2)中解决问题非常简单,但是对于这个问题有没有O(n)解?
注意:这是一个面试问题,问我的一个朋友,面试官很确定它可以在O(n)时间内解决 .
4 回答
正确的O(n)解决方案非常复杂,需要大量的文本,代码和技巧来解释和证明 . 更确切地说,令人信服地需要3页,这里可以详细查看http://www.cse.yorku.ca/~andy/pubs/X+Y.pdf(在评论中找到
simonzack
) .它基本上是一个聪明的分而治之算法,除其他外,它利用了这样一个事实:在一个排序的n乘n矩阵中,人们可以在
O(n)
找到小于/大于给定的元素数量号码k
. 它递归地将矩阵分解为较小的子矩阵(通过仅取奇数行和列,产生具有n/2
列和n/2
行的子矩阵),结合上述步骤,导致复杂度为O(n) + O(n/2) + O(n/4)... = O(2*n) = O(n)
. 太疯狂了!我无法解释它比论文更好, which is why I'll explain a simpler, O(n logn) solution instead :) .
O(n * logn)解决方案:
It's an interview! 你无法及时得到那个
O(n)
解决方案 . 嘿,为什么不提供一个解决方案,虽然不是最优的,但表明你可以做得比其他明显的候选人更好?我将利用上面提到的
O(n)
算法来查找在排序的n-by-n
矩阵中小于/大于给定数字k
的数字量 . 请记住,我们不需要实际的矩阵!由OP描述的两个大小为n
的数组的笛卡尔和,得到一个排序的n-by-n
矩阵,我们可以通过考虑数组的元素来模拟如下:因此,每行包含非递减数字,每列也包含非递减数字 . 现在,假装给你一个号码
k
. 我们想在O(n)
找到这个矩阵中有多少数字小于k
,有多少数字更大 . 显然,如果两个值都小于(n²+1)/2
,那意味着k
是我们的中位数!算法非常简单:
这基本上计算了每行符合条件的元素数量 . 由于行和列已按上面所示排序,因此这将提供正确的结果 . 由于
i
和j
每次最多迭代n
次,算法为O(n)
[注意j
不会在for
循环内重置] .greater_than_k
算法类似 .现在,我们如何选择
k
?那是logn
部分 . Binary Search! 正如其他答案/评论中所提到的,中位数必须是此数组中包含的值:candidates[n] = {a[0]+b[n-1], a[1]+b[n-2],... a[n-1]+b[0]};
.只需对此数组[也
O(n*logn)
]进行排序,然后对其运行二进制搜索 . 由于数组现在处于非递减顺序,因此可以直截了当地注意到小于每个candidate[i]
的数字量也是非递减值(单调函数),这使得它适合于二进制搜索 . 其结果smaller_than_k(k)
返回小于(n²+1)/2
的最大数k = candidate[i]
是答案,并且在log(n)
迭代中获得:假设数组是
A = {A[1] ... A[n]}
和B = {B[1] ... B[n]}
,成对和数组是C = {A[i] + B[j], where 1 <= i <= n, 1 <= j <= n}
,它有n^2
元素,我们需要找到它的中位数 .C
的中位数必须是数组D = {A[1] + B[n], A[2] + B[n - 1], ... A[n] + B[1]}
的一个元素:如果你修复A[i]
,并考虑所有的总和A[i] + B[j]
,你会看到唯一的A[i] + B[j = n + 1 - i]
(D
之一)可能是中位数 . 也就是说,它可能不是中位数,但如果不是,那么所有其他A[i] + B[j]
也不是中位数 .这可以通过考虑所有
B[j]
来计算,并计算 lower 的值的数量和 greater 的值的数量,而不是A[i] + B[j]
(我们可以非常准确地做到这一点,因为这两个数组已经排序 - 计算有点混乱的想法) . 你会看到A[i] + B[n + 1 - j]
这两个计数最多"balanced" .然后问题减少到找到中位数
D
,只有n
个元素 . 诸如Hoare's的算法将起作用 .UPDATE :这个答案错了 . 这里真正的结论是 median 是
D
元素中的一个,但是D
的中位数与C
的中位数不同 .这不行吗?:
只要
A
和B
已排序,您就可以在线性时间内计算数字的等级 . 您用于计算排名的技术也可用于查找A+B
中的所有内容,这些内容在时间的某个下限和某个上限之间,与输出的大小相加,加上|A|+|B|
.从
A+B
随机抽样n
. 取中位数,比如foo
. 计算foo
的等级 . 在概率恒定的情况下,foo
的等级在中位数的n
之内 . 继续这样做(预期的常数次数),直到你的中位数的下限和上限在彼此的2n
之内 . (整个过程需要预期的线性时间,但显然很慢 . )您现在要做的就是枚举边界之间的所有内容,并在线性大小的列表上进行线性时间选择 .
(不相关的,我不会原谅面试官问这么明显糟糕的面试问题 . 这样的东西绝不表示你的编码能力 . )
EDIT :您可以通过执行以下操作来计算数字
x
的等级:FURTHER EDIT :实际上,上述技巧只会将候选空间缩小到大约n个log(n)
A+B
成员 . 然后在大小为n log(n)的Universe中存在一般选择问题;你可以再做一次基本相同的技巧,找到一个与sqrt(n)log(n)成比例的范围 .原因如下:如果您从n组中采样k并获取中位数,则样本中位数的顺序介于(1/2 - sqrt(log(n)/ k))和(1/2 sqrt( log(n)/ k))具有至少恒定概率的元素 . 当n = | A B |时,我们要取k = sqrt(n),我们得到一个大约sqrt(n log n)个元素的范围---这是关于| A | log | A | . 但是你再次这样做,你得到的是sqrt(n)polylog(n)的范围 .
您应该使用选择算法来查找O(n)中未排序列表的中位数 . 看看这个:http://en.wikipedia.org/wiki/Selection_algorithm#Linear_general_selection_algorithm_-_Median_of_Medians_algorithm