zoukankan      html  css  js  c++  java
  • LeetCode 4: Median of Two Sorted Arrays

    Description:

    There are two sorted arrays nums1 and nums2 of size m and n respectively.

    Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

    You may assume nums1 and nums2 cannot be both empty.

    Example 1:

    nums1 = [1, 3]
    nums2 = [2]
    
    The median is 2.0
    

    Example 2:

    nums1 = [1, 2]
    nums2 = [3, 4]
    
    The median is (2 + 3)/2 = 2.5

    描述:

    两个有序数组nums1和nums2,数组元素数量分别为m和n。

    寻找两个数组的中位数。要求程序的时间复杂度为O(lg(m+n))。

    假设数组nums1和nums2不同时为空。

    示例1:

      nums1 = [1, 3]

      nums2 = [2]

      中位数为2.0

    示例2:

      nums1 = [1, 2]

      nums2 = [3, 4]

      中位数为(2 + 3) / 2 = 2.5

    方法:

    首先,我们先理解中位数的概念和意义。中位数,又称中点数,中值。中数是按顺序排列的一组数据中居于中间位置的数,即在这组数据中,有一半的数据比他大,有一半的数据比他小。对于有限的数集,可以通过把所有观察值高低排序后找出正中间的一个作为中位数。如果观察值有偶数个,通常取最中间的两个数值的平均数作为中位数。

    中位数的意义是把一个集合分为两个长度相等的子集合,其中一个子集合总是比另外一个集合大。

    假设题中两个数组nums1和nums2分别以集合A和B表示。

    首先,在随机位置i把集合A分成两个子集合。

            left_A          |           right_A
    A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m - 1]

    由于集合A的大小为m,因此共有(m+1)中划分方法(i = 0 ~ m)。

    子集合left_A的长度为i,子集合right_A的长度为m - i。注意,当i=0时,left_A为空,当i=m时,right_A为空。

    同样地,在随机位置j将集合B分为两个子集合。

            left_B          |           right_B
    B[0], B[1], ..., B[j-1] | B[i], B[i+1], ..., B[n - 1]

    将子集合left_A和left_B放在一个子集合中,将right_A和right_B组成一个子集合,分别称为left_part和right_part。

         left_part          |         right_part
    A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m - 1]
    B[0], B[1], ..., B[j-1] | B[i], B[i+1], ..., B[n - 1]

    如果可以保证:

    len(left_part)=len(right_part)
    max(left_part) ≤ min(right_part)

    那么,我们将集合A和集合B分割为两个长度相等的部分,并且left_part总是小于right_part。

    并且:

    median= (max(left_part)+min(right_part))/2

    为了保证上述条件成立,我们需要保证:

    1. i+j=m−i+n−j (or: m - i + n - j + 1)
       if n≥m, we just need to set:  i=0∼m, j=(m+n+1)/2−i
    
    2. B[j−1]≤A[i] and A[i−1]≤B[j]

    PS1: 为了便于表述,假设A[i-1]、B[j-1]、A[i]和B[j]有效,即使i=0,i=m,j=0和j=n时。后面将阐述如何处理这些情况。

    PS2: 为什么要求n>=m?主要是确保j的值非负。由于0im,并且j = (m + n + 1)/2- i。如果n<m, 那么j的值可能为负,导致结果错误。

    综上所述,整体思路是:

    在区间[0,m]中寻找i的值,使得

    B[j−1]≤A[i] and A[i−1]≤B[j],  这里, j = (m + n + 1)/2 - i

    可以使用二叉查找的方式查询满足上述条件的i值。

    1. 设置imin=0, imax=m,并在[imin,imax]区间搜索;
    2. 设置i=(imin + imax)/2, j = (m + n + 1)/2 - i;
    3. 此时,len(left_part) = len(right_part)。并且会有如何三种情形。
        a. B[j - 1] <= A[i]且A[i-1] <= B[j]。此时,我们找到正确的结果,停止搜索并退出。
        b. B[j - 1] > A[i]。此时,A[i]的值较小,需要调整i的值使得B[j-1]<=A[i]。增加i的值,导致A[i]的值增加,并且j的值减小,B[j-1]的值也相应减小。开始搜索区间[i + 1, imax]。设置imin的值为i+1,跳转到步骤2。
        c. A[i - 1] > B[j]。此时,以为这A[i-1]的值较大,需要减少i的值,并开始搜索区间[imin, i - 1]。设置imax的值为i-1,并跳转到步骤2。

    当寻找到满足上述条件的i值后,中间值的计算方式如下:

    max(A[i−1],B[j−1]),  当m + n为奇数时​;
    ​(max(A[i−1],B[j−1])+min(A[i],B[j])),当n为偶数时。

    现在,我们开始考虑边际条件。即i=0或i=m或j=0或j=n时,此时A[i-1]、A[i]、B[j-1]或B[j]的值可能并不存在。

    我们需要保证max(A[i−1],B[j−1])<=min(A[i],B[j])。所以当i和j的值均不是边际值时,需要检查B[j-1]<=A[i]且A[i-1]<=B[j]。

    如果i或j的值为边际值,那么我们不需要检查相应的边际条件。例如, 如果 i=0, 那么A[i1]不存在。此时,我们不需要检查A[i1]B[j];我们需要做的是:

    在区间[0,m]中搜索i的值使得
    (j=0或i = m或B[j−1]≤A[i])并且
    (i=0或j = n或A[i−1]≤B[j])

    在每次搜索循环中,我们遇到以下三种情形:

    1. (j=0或i=m或B[j−1]≤A[i]) 并且(i=0或j=n或A[i−1]≤B[j]) 
       表明i为所需要寻找的值,停止搜索。
    2. j>0且i<m且B[j−1]>A[i] 
        表明i的值较小,增加i的值
    3. i>0且j<n且A[i−1]>B[j] 
        表明i的值较大,减少i的值

    此外,由于i<mj>0且i>0j<n。所以在情形2和情形3下我们不需要检查j>0和i>0的条件。

    class Solution {
    public:
        double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
            int m = nums1.size();
            int n = nums2.size();
            if(m > n) {
                vector<int> temp = nums1;
                nums1 = nums2;
                nums2 = temp;
                int t = m;
                m = n;
                n = t;
            }
            
            int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
            while(iMin <= iMax) {
                int i = (iMin + iMax) / 2;
                int j = halfLen - i;
                if(i < iMax && nums2[j - 1] > nums1[i]){
                    iMin = i + 1;
                } else if(i > iMin && nums1[i - 1] > nums2[j]) {
                    iMax = i - 1;
                } else {
                    int maxLeft = 0;
                    if(i == 0) maxLeft = nums2[j - 1];
                    else if(j == 0) maxLeft = nums1[i - 1];
                    else maxLeft = max(nums1[i - 1], nums2[j - 1]);
                    if((m + n) % 2 != 0) return maxLeft;
                    
                    int minRight = 0;
                    if(i == m) minRight = nums2[j];
                    else if(j == n) minRight = nums1[i];
                    else minRight = min(nums1[i], nums2[j]);
                    
                    return ((double)minRight + maxLeft) / 2.0;
                }
            }
            return 0.0;
        }
    };

    本方法的时间复杂度为O(log(min(m,n)),空间复杂度为O(1)。

  • 相关阅读:
    Saltstack module acl 详解
    Saltstack python client
    Saltstack简单使用
    P5488 差分与前缀和 NTT Lucas定理 多项式
    CF613D Kingdom and its Cities 虚树 树形dp 贪心
    7.1 NOI模拟赛 凸包套凸包 floyd 计算几何
    luogu P5633 最小度限制生成树 wqs二分
    7.1 NOI模拟赛 dp floyd
    springboot和springcloud
    springboot集成mybatis
  • 原文地址:https://www.cnblogs.com/alpaca/p/9475174.html
Copyright © 2011-2022 走看看