找到两个排序数组的合并数组的中位数

时间:2017-05-31 02:37:31

标签: algorithm data-structures time-complexity median

假设我们有2个排序的整数数组,大小为n和m。找到所有m + n个数字的中位数的最佳方法是什么?

log(n) * log(m)复杂度很容易做到这一点。但我想在log(n) + log(m)时间内解决这个问题。那么有什么建议可以解决这个问题吗?

4 个答案:

答案 0 :(得分:1)

<强>解释

这个问题的关键是通过比较剩余A和B的中位数来逐步忽略A和B的一半:

if (aMid < bMid) Keep [aMid  +1 ... n] and [bLeft ... m]    
else Keep [bMid + 1 ... m] and [aLeft ... n]
// where n and m are the length of array A and B

如下所示:时间复杂度为O(log(m + n))

public double findMedianSortedArrays(int[] A, int[] B) {
    int m = A.length, n = B.length;
    int l = (m + n + 1) / 2;
    int r = (m + n + 2) / 2;
    return (getkth(A, 0, B, 0, l) + getkth(A, 0, B, 0, r)) / 2.0;
}

public double getkth(int[] A, int aStart, int[] B, int bStart, int k) {
    if (aStart > A.length - 1) return B[bStart + k - 1];            
    if (bStart > B.length - 1) return A[aStart + k - 1];                
    if (k == 1) return Math.min(A[aStart], B[bStart]);

    int aMid = Integer.MAX_VALUE, bMid = Integer.MAX_VALUE;
    if (aStart + k/2 - 1 < A.length) aMid = A[aStart + k/2 - 1]; 
    if (bStart + k/2 - 1 < B.length) bMid = B[bStart + k/2 - 1];        

    if (aMid < bMid) 
        return getkth(A, aStart + k / 2, B, bStart, k - k / 2); // Check: aRight + bLeft 
    else 
        return getkth(A, aStart, B, bStart + k / 2, k - k / 2); // Check: bRight + aLeft
}

希望它有所帮助!如果您需要更多解释,请告诉我。

答案 1 :(得分:1)

Here's a very good solution I found in Java on Stack Overflow.这是一种在两个数组中找到K和K + 1最小项的方法,其中K是合并数组的中心。

如果你有一个函数来找到两个数组的Kth项,那么找到两者的中位数很容易;

  1. 计算第K和第K + 1项X和Y
  2. 的加权平均值

    但是你需要一种方法来找到两个列表的Kth项目; (记住我们现在是一个索引)

    1. 如果X包含零项,那么X和Y的第K个最小项是Y的第K个最小项

    2. 否则,如果K == 2则X和Y的第二个最小项是X和Y中最小的项(min(X [0],Y [0]))

    3. 否则;

      我。设A为min(长度(X),K / 2)

      II。设B为min(长度(Y),K / 2)

      III。如果X [A]>然后Y [B]从步骤1中用X,Y'递归,其中Y的所有元素从B到Y的末尾,K'= K-B,否则用X'递归,其中X的所有元素从A到结尾X,Y和K'= K - A

    4. 如果我明天找到时间,我将验证此算法是否按照规定在Python中运行并提供示例源代码,它可能会出现一些一对一的错误。

答案 2 :(得分:0)

获取列表A中的中间元素并将其命名为a。将a与列表B中的中心元素进行比较。让我们将它们称为b1和b2(如果B具有奇数长度,则精确分割b的位置取决于您对偶数长度列表的中位数的定义,但无论如何程序几乎相同)。如果b1≤a≤b2则a是合并数组的中值。这可以在恒定的时间内完成,因为它只需要两次比较。

如果a大于b2,那么我们将A的上半部分添加到B的顶部并重复。 B将不再排序,但并不重要。如果a小于b1,那么我们将A的下半部分添加到B的底部并重复。这些将最多迭代log(n)次(如果找到中位数然后停止,当然)。

这可能无法找到中位数。如果是这种情况,则中位数在B中。如果是,则执行与A和B相反的相同算法。这将需要log(m)迭代。总共你将执行最多2 *(log(n)+ log(m))迭代的恒定时间操作,因此你已经按log(n)+ log(m)时间顺序解决了这个问题。

这与iehrlich给出的答案基本相同,但更明确地写出来。

答案 3 :(得分:-2)

是的,这可以做到。给定两个数组AB,在最坏的情况下,您必须首先在A中执行二进制搜索,然后,如果失败,则在{{1}中进行二进制搜索寻找中位数。在二进制搜索的每一步,您检查当前元素是否实际上是合并的B数组的中位数。这种检查需要一段时间。

让我们看看为什么这样的检查是不变的。为简单起见,我们假设A+B是一个奇数,并且两个数组中的所有数字都不同。您可以稍后通过应用通常的中值定义方法(即,如何计算包含重复项的数组的中位数或具有偶数长度的数组的中值)来消除这些限制。无论如何,鉴于此,我们肯定知道,在合并数组中,实际中位数的右侧和左侧将有|A| + |B|个元素。在(|A| + |B| - 1) / 2中的二进制搜索过程中,我们知道数组A中当前元素x的索引(让它为A)。现在,如果i满足条件xB[j] < x < B[j+1],那么i + j == (|A| + |B| - 1) / 2就是您的中位数。

总体复杂度为x时间和O(log(max(|A|, |B|))内存。