二分 - 寻找两个有序数组的中位数 - Leetcode 4

Posted njuptACMcxk

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二分 - 寻找两个有序数组的中位数 - Leetcode 4相关的知识,希望对你有一定的参考价值。

二分 - 寻找两个有序数组的中位数 - Leetcode 4

给定两个大小分别为 mn 的正序(从小到大)数组 AB。请你找出并返回这两个正序数组的 中位数

示例 1:

输入:A = [1,3], B = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2

示例 2:

输入:A = [1,2], B = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

示例 3:

输入:A = [], B = [1]
输出:1.00000

示例 4:

输入:A = [2], B = []
输出:2.00000

方法一:划分数组( O ( log ⁡ 2 ( m i n ( n , m ) ) ) O(\\log_2(min(n,m))) O(log2(min(n,m)))

我们可以把两个数组看成是一个数组的两段,然后设法在 A 或者 B 中找出中位数所在的位置。

这个做法需要理解中位数的作用:它把一个数组划分成了等长的两段。

所以,我们首先设法把两个数组重新划分成等长的两段,假设是 left 段 和 right 段,使得 left 段的最大值不超过 right 段的最小值:

        left            |           right
A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[n-1]
B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[m-1]

即:

  • l e n ( l e f t ) = i + j = l e n ( r i g h t ) = n + m − i − j + [ n + m   是 奇 数 ] len(left)=i+j=len(right)=n+m-i-j+[n+m\\ 是奇数] len(left)=i+j=len(right)=n+mij+[n+m ]
  • max ⁡ ( l e f t ) ≤ min ⁡ ( r i g h t ) \\max(left)\\le \\min(right) max(left)min(right)

那么答案为:

n + m 为 偶 数 : A n s = max ⁡ ( l e f t ) + min ⁡ ( r i g h t ) 2 n+m为偶数:Ans=\\cfrac{\\max(left)+\\min(right)}{2} n+mAns=2max(left)+min(right)

n + m 为 奇 数 : A n s = max ⁡ ( l e f t )   — — ( 左 边 多 一 个 元 素 ) n+m为奇数:Ans=\\max(left)\\ ——(左边多一个元素) n+mAns=max(left) 

具体实现细节如下:

  • 我们首先要在数组 A 中二分出划分的位置 i i i,满足: max ⁡ ( l e f t ) ≤ min ⁡ ( r i g h t ) \\max(left)\\le \\min(right) max(left)min(right),即:
    max ⁡ ( A [ i − 1 ] , B [ j − 1 ] ) ≤ min ⁡ ( A [ i ] , B [ j ] ) \\max(A[i-1],B[j-1])\\le \\min(A[i],B[j]) max(A[i1],B[j1])min(A[i],B[j])
    等价于: B [ j − 1 ] ≤ A [ i ]    & &    A [ i − 1 ] ≤ B [ j ] B[j-1]\\le A[i] \\ \\ \\&\\&\\ \\ A[i-1]\\le B[j] B[j1]A[i]  &&  A[i1]B[j]
    我们仔细思考发现,上述条件是满足”单调性的“。因为,我们多分给 left 一个元素,就少分给right 一个元素。即, i i i 递增时, j j j 相应地递减,也就是 A [ i ] A[i] A[i] 递增, B [ j ] B[j] B[j] 就递减。那么可以得出结论: A [ i ] A[i] A[i] 越大越好,越容易满足约束条件。
    所以,我们可以二分出最大的,满足条件的 A [ i ] A[i] A[i]
  • 注意,根据等长的条件,得到: j = ⌊ m + n + 1 2 ⌋ − i j=\\lfloor{\\cfrac{m+n+1}{2}}\\rfloor-i j=2m+n+1i,为了保证 j ≥ 0 j\\ge 0 j0,简化边界的判断,我们可以假设 m ≤ n m\\le n mn,即 A 的长度 ≤ B 的长度。
  • 然后我们维护出 max ⁡ ( l e f t ) \\max(left) max(left) min ⁡ ( r i g h t ) \\min(right) min(right)
  • 最后根据奇偶性输出答案即可。

代码:

class Solution {
public:
     double findMedianSortedArrays(vector<int>& A, vector<int>& B) 
     {
        int m = A.size(), n = B.size();
        if(m > n) swap(A, B), swap(m, n);
        
        int l = 0, r = m;
        int max_left = 0, min_right = 0;
        int A_i, A_im1, B_j, B_jm1;
        int i, j;
        
        while(l < r)
        {
            i = l + r + 1 >> 1;
            j = (n + m + 1 >> 1) - i;
            
            // 事实上这里 i > 0 是必然成立的,相对的,j < n 也是必然成立的。
            if(A[i - 1] <= B[j]) l = i;
            else r = i - 1;
        }
        
        j = (n + m + 1 >> 1) - l;
        A_i = (l == m ? INT_MAX : A[l]);
        A_im1 = (l == 0 ? INT_MIN : A[l - 1]);
        B_j = (j == n ? INT_MAX : B[j]);
        B_jm1 = (j == 0 ? INT_MIN : B[j - 1]);
        
        max_left = max(A_im1, B_jm1);
        min_right = min(A_i, B_j);
        
        return (n + m & 1) ? max_left : (double)(max_left + min_right) / 2.0;
    }
};

以上是关于二分 - 寻找两个有序数组的中位数 - Leetcode 4的主要内容,如果未能解决你的问题,请参考以下文章

[leetcode]4寻找两个有序数组的中位数

Leetcode 寻找两个有序数组的中位数

Leetcode4. 寻找两个正序数组的中位数(二分)

Leet Code 4.寻找两个有序数组的中位数

每天一道Leecode——寻找两个正序数组的中位数,腾讯高频面试题分享!

[二分查找] 两个等长有序数组的上中位数