【问题标题】:Understanding the algorithm of Median of Two Sorted Arrays理解两个有序数组中位数的算法
【发布时间】:2016-03-10 06:45:14
【问题描述】:

有两个排序数组 A 和 B,大小分别为 m 和 n。找到两个排序数组的中位数。总体运行时间复杂度应为 O(log (m+n))。

我不明白计算 aMid 和 bMid 的公式。这些公式背后的逻辑是什么?

int aMid = aLen * k / (aLen + bLen); // a 的中间数

int bMid = k - aMid - 1; // b 的中间数

这是程序的链接。 http://www.programcreek.com/2012/12/leetcode-median-of-two-sorted-arrays-java/][1]

public static double findMedianSortedArrays(int A[], int B[]) {
    int m = A.length;
    int n = B.length;

    if ((m + n) % 2 != 0) // odd
        return (double) findKth(A, B, (m + n) / 2, 0, m - 1, 0, n - 1);
    else { // even
        return (findKth(A, B, (m + n) / 2, 0, m - 1, 0, n - 1) 
            + findKth(A, B, (m + n) / 2 - 1, 0, m - 1, 0, n - 1)) * 0.5;
    }
}

public static int findKth(int A[], int B[], int k, 
    int aStart, int aEnd, int bStart, int bEnd) {

    int aLen = aEnd - aStart + 1;
    int bLen = bEnd - bStart + 1;

    // Handle special cases
    if (aLen == 0)
        return B[bStart + k];
    if (bLen == 0)
        return A[aStart + k];
    if (k == 0)
        return A[aStart] < B[bStart] ? A[aStart] : B[bStart];

    int aMid = aLen * k / (aLen + bLen); // a's middle count    
                                      // I AM STUCK HERE

    int bMid = k - aMid - 1; // b's middle count

    // make aMid and bMid to be array index
    aMid = aMid + aStart;
    bMid = bMid + bStart;

    if (A[aMid] > B[bMid]) {
        k = k - (bMid - bStart + 1);
        aEnd = aMid;
        bStart = bMid + 1;
    } else {
        k = k - (aMid - aStart + 1);
        bEnd = bMid;
        aStart = aMid + 1;
    }

    return findKth(A, B, k, aStart, aEnd, bStart, bEnd);
}

我从带有代码的 cmets 中得到了一些想法,这些公式是如何计算的,但仍然不明白向某人解释“为什么要使用这些公式”或者这些公式背后的逻辑是什么?

对于 int aMid = aLen * k / (aLen + bLen); // a 的中间数 作为 aMid = aLen / 2 --(i)

and k = (aLen + bLen)/2, -->2 = (aLen + bLen)/k

将 2 的值放入 equ (i)

所以 aMid = aLen/(aLen + bLen)/k== aLen *k/ (aLen+bLen)

对于 int bMid = k - aMid - 1; // b 的中间计数

aMid + bMid + 1 = k 必须满足才能得出 A[aMid] > B[bMid] 时的结论

至于为什么 aMid + bMid + 1 = k 显着:如果 A[aMid] 大于 B[bMid],你知道 A 中 A[aMid] 之后的任何元素都不能是第 k 个元素,因为B 中有太多元素低于它(并且会超过 k 个元素)。您还知道 B[bMid] 和 B[bMid] 之前的任何元素都不能是第 k 个元素,因为 A 中低于它的元素太少(在 B[bMid] 之前没有足够的元素是第 k 个元素)。

【问题讨论】:

  • 解决问题的最佳方法是尝试自己解决问题。你会怎么做?如果您假设两个数组具有相同的数字分布,那么从中间开始的最佳猜测是什么?
  • 您的第二行不正确。递归调用此方法时,k 可能不同。
  • @PeterLawrey 我试着自己做,但我完全不知道该怎么做。还有其他计算中位数的算法,我完全理解它们,但他们找到了两个相等数组的中位数。我喜欢这个,因为它是更通用的算法。第 2 行是不正确的?是的,你是对的,当递归调用此方法时,k 可能会有所不同。
  • 你解释的第二行。您假设 k 始终以相同的方式计算。
  • @peterLawrey 是的,你是对的,我的假设是错误的。

标签: java arrays algorithm


【解决方案1】:

正如您已经提到的:aMid + bMid + 1 = k 必须满足才能得出以下结论:
A[aMid] &gt; B[bMid] 我们可以扔掉bMid 之前的所有东西以及aMid 之后的所有东西(包括),
因为我们知道有bMid + aMid + 1(包括aMid= k元素小于A[aMid]。因此,我们的中位数位于剩余的数组中。

考虑到这一点,我们首先如何设置我们的两个中间值aMidbMid 并不重要。唯一需要注意的是不要让其中一个引起IndexOutOfBoundsException

int aMid = 0;
int bMid = k - aMid - 1;
if(bMid >= bLen) {
    bMid = bLen - 1;
    aMid = k - bMid - 1;
}

也可以解决问题。但这将花费超过O(log(n+m)) 的时间,因为在最坏的情况下,我们只会跳过一个元素 (A[0])。
我们想要的是总是扔掉一部分aLen + bLen
在我们的例子中是:

A > B:k = k - (bMid +1) = k - (k - aMid) = aMid = k * (aLen / (aLen + bLen))
B > A:k = k - (aMid + 1) = k - (k * aLen / (aLen + bLen)) -1 = k * (bLen / (aLen + bLen)) - 1

忽略 -1 并假设A &gt; B 的概率与B &gt; A 相同,我们得到:
E(k) = 0.5 * k * (aLen/(aLen + bLen)) + 0.5 * k * (bLen/(aLen + bLen))
= 0.5 * k (aLen + bLen)/(aLen + bLen) = 0.5 * k
这意味着我们得到大约O(log(n + m)) 递归调用,直到k 为0,然后函数停止。

【讨论】:

  • 我对 k = k - (bMid - bStart + 1); 这行有点困惑由于 (A[aMid] > B[bMid]) 所以 bMid + bMid 之前的所有值本身不能是 K 所以从 K 中减去它但是为什么要加 1?我对 1 完全感到困惑。
  • 我们的值B[bStart], B[bStart+1], B[bStart+2], ..., B[bMid] 都小于A[aMid]。这些是bMid - bStart + 1 元素。示例:B[3], B[4], B[5], B[6]6 - 3 + 1 = 4 元素。 Whitout +1 我们得到3
猜你喜欢
  • 2012-09-17
  • 2012-01-18
  • 1970-01-01
  • 2013-09-18
  • 2014-11-02
  • 1970-01-01
  • 2012-09-14
  • 1970-01-01
相关资源
最近更新 更多