理解“两个排序数组的中位数”的算法

Posted

技术标签:

【中文标题】理解“两个排序数组的中位数”的算法【英文标题】:Understanding the algorithm of "Median of Two Sorted Arrays" 【发布时间】:2012-09-17 01:41:53 【问题描述】:

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

double findMedianSortedArrays(int A[], int m, int B[], int n) 
    return findMedianHelper2(A, m, B, n, max(0, (m-n)/2), min(m-1, (m+n)/2));


double findMedianHelper2(const int A[], const int m, const int B[], const int n, const int l, const int r) 
    if (l > r) return findMedianHelper2(B, n, A, m, max(0, (n-m)/2), min(n-1, (m+n)/2));

    int i = (l+r)/2;
    int j = (m+n)/2-i;

    assert(i >= 0 && i <= m && j >= 0 && j <= n);
    int Ai_1 = ((i == 0) ? INT_MIN : A[i-1]);
    int Bj_1 = ((j == 0) ? INT_MIN : B[j-1]);
    int Ai = ((i == m) ? INT_MAX : A[i]);
    int Bj = ((j == n) ? INT_MAX : B[j]);

    if (Ai < Bj_1) return findMedianHelper2(A, m, B, n, i+1, r);
    if (Ai > Bj) return findMedianHelper2(A, m, B, n, l, i-1);

    if (((m+n) % 2) == 1) return A[i];
    return (max(Ai_1, Bj_1) + Ai) / 2.0;

问题:选择l = max(0, (m-n)/2)r = min(m-1, (m+n)/2)是什么意思

谢谢

【问题讨论】:

您可能会从this question 那里得到一些更笼统的说明。 另见***.com/questions/6182488 【参考方案1】:

那个代码对我来说没有意义。但是,我认为这里的关键是确保 m>n 和值 (m-n)/2 和 (m+n)/2 正确传递给辅助函数。 此外,从辅助函数开头的 if 语句中,我们可以看出其意图是在 m 时进行修复

假设 m>0 和 n>0(它们必须如此才能使数组有意义。) 如果 m>n,那么在 helper 内部,(l>r) 将为 false,算法应该可以正常工作。 如果 mr) 将为 false(除非 m=1),并且“修复”似乎根本无法修复任何东西。

因此,我认为代码一开始就有问题。 但是,主要部分对我来说似乎很有意义,并且确实帮助我实现了在 JAVA 中做同样的事情。

【讨论】:

对不起,这是我第一次在 *** 上发帖,我没有意识到我正在输入 html 代码。【参考方案2】:

问题>选择l = max(0, (m-n)/2) and r = min(m-1, (m+n)/2)是什么意思

MAX 和 MIN 用于限制值,因此它们不能低于或高于约束。

IF m - n < 0 THEN
    l = 0
ELSE l = (m - n) / 2

IF (m + n) / 2 > m - 1 THEN
    r = m -1
ELSE r = (m + n) / 2

【讨论】:

这是一个很好的代码解释,但它不能解决我的问题。我很难理解为什么我们可以在这个算法中应用这个约束。这个设置背后的理论是什么。 -thx @q0987 - 也许您可以将每次迭代的值输出到控制台或调试,这样您就可以掌握如何使用 l 和 r。当 n 和 m 不相等时,l 和 r 充当校正措施。这允许 i 和 j 始终在数组的范围内(因此是断言)。 事实上,在我在这里发布问题之前,我已经完成了这些步骤。我可以通过调试代码来了解它是如何工作的。但我不知道为什么我们可以为 l 和 r 做出这样的选择。 @q0987 - 您有 2 个排序列表,如果将它们组合成 1 个长列表,找到中值会很容易,但是组合步骤必须遍历它们(破坏 Log N 时间。因此,您可以通过列表进行比较(Ai,Bj,Ai_1,Bj_1 来尝试导出列表的中心,如果它们是一个大列表(而不是两个较小的列表)。【参考方案3】:

首先。让我们证明 m=n 情况下的算法。 将中间元素命名为“k”

m1:=A[n/2]

m2:=B[n/1]`

如果 m1

证明:m1 k。

如果 m1 > k 与我们得到 m2

A 和 B 合并的中间元素将以相同的方式成为 A/2 和 B/2 合并的中间元素。 所以我们需要继续在两个数组中查找元素:A/2 和 B/2 所以转到 1) 项,直到数组相等。

【讨论】:

【参考方案4】:

http://leetcode.com/2011/03/median-of-two-sorted-arrays.html 这是该算法的分析细节

【讨论】:

【参考方案5】:

选择这样的左右索引的原因是为了跳过不能是两个排序数组中位数的元素。

不失一般性,我们假设m &gt; n。那么有两种极端情况:

即使B中的所有元素都小于A[0],中位数仍然不可能是A[0, ... , (m - n) / 2 - 1]中的元素,因为n + (m - n) / 2 - 1 &lt; (m + n) / 2。 同样,即使 B 中的所有元素都大于 A[m - 1],中位数仍然不可能是 A[(m + n) / 2 + 1, ... , m - 1] 内的元素,因为 A[(m + n) / 2] 必须是中位数。

基于这个观察,我们只需要对较长数组的一个子数组进行二分查找,就可以找到中位数。

对于m &lt; nl = max(0, (m-n)/2) = 0r = min(m-1, (m+n)/2) = m - 1,这实质上意味着中位数可能是较短数组中的任何元素。

【讨论】:

以上是关于理解“两个排序数组的中位数”的算法的主要内容,如果未能解决你的问题,请参考以下文章

Leetcode-两个排序数组的中位数

两个排序数组的中位数

LeetCode刷题-004两个排序数组的中位数

[LeetCode] 4. 两个排序数组的中位数

leetcode 两个排序的中位数 python

#yyds干货盘点#两个排序数组的中位数,“最”有技术含量的解法