• 4. 寻找两个有序数组的中位数(log(min(n,m)))实现


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

    给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。

    请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

    你可以假设 nums1 和 nums2 不会同时为空。

    示例 1:

    nums1 = [1, 3]
    nums2 = [2]

    则中位数是 2.0
    示例 2:

    nums1 = [1, 2]
    nums2 = [3, 4]

    则中位数是 (2 + 3)/2 = 2.5

    为了解决这个问题,我们需要理解 “中位数的作用是什么”。在统计中,中位数被用来:

    将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。

    这其中又分为偶数组和奇数组:

    奇数组:[2 3 5] 对应的中位数为3

    偶数组: [1 4 7 9] 对应的中位数为(4 + 7) /2 = 5.5

    先解释下“割”
    我们通过切一刀,能够把有序数组分成左右两个部分,切的那一刀就被称为割(Cut),割(Cut)的左右会有两个元素,分别是左边最大值和右边最小值。

    我们定义LMax= Max(LeftPart),RMin = Min(RightPart)。

    割可以割在两个数中间,也可以割在1个数上,如果割在一个数上,那么这个数即属于左边,也属于右边

    奇数组:[2 3 5] 对应的中位数为3,假定割(Cut)在3上,我们可以把3分为2个:[2 (3/3) 5]

    因此LMax=3, RMin=3

    偶数组: [1 4 7 9] 对应的中位数为(4 + 7) /2 = 5.5,假定割(Cut)在4和7之间:[1 (4/7) 9]

    因此LMax=4, RMin=7

    割和第k个元素
    一个数组
    对于一个有序数组,对于数组A,如果在k的位置割(Cut)一下(不是割(Cut)在两数中间),那么LMax = RMin = A[k],

    两个数组
    也就是我们题目的状态,我们要求得两个数组合并成一个有序数组时,第k位的元素

    我们设:
    Ci为第i个数组的割。

    LMaxi为第i个数组割后的左元素.

    RMini为第i个数组割后的右元素。 

    首先,LMax1<=RMin1,LMax2<=RMin2 这是肯定的,因为数组是有序的,左边肯定小于右边!,而如果割(Cut)在某个数上,则左右相等。

    其次,如果我们让LMax1<=RMin2,LMax2<=RMin1 呢

     

    那么如果左半边全小于右半边,如果左边的元素个数相加刚好等于k, 那么第k个元素就是Max(LMax1, LMax2),这个比较好理解的,因为Max(LMax1, LMax2)肯定是左边k个元素的最大值,因为合并后的数组是有序,第k个元素肯定前面k个元素中最大的那个。

    那么如果 LMax1>RMin2,说明数组1的左边元素太大(多),我们把C1减小,C2=k-C1也就相应的增大。LMax2>RMin1同理,把C2减小,C1=k-C2也就相应的增大。

    假设k=3

    对于

    [2 3 5]

    [1 4 7 9]
    设C1 = 1, 那么C2 = k - C1 = 2

    [2 / 3 5]

    [1 4 / 7 9]

    这时LMax1 =2, RMin1 = 3, LMax2=4, RMin2=7,

    从而有LMax2 > RMin1,依据前面的推论,我们要将C1增大,所以我们让C1 = 2,如下:

    [2 3 /5]

    [1 / 4 7 9]

    这时LMax1 =3, RMin1 = 5, LMax2=1, RMin2=4, 满足 LMax1 < RMin2 且 LMax2 < RMin1, 所以第3个元素为Max(LMax1,LMax2) = 3

    两个数组的最大问题是,它们合并后,m+n总数可能为奇, 也可能为偶,所以我们得想法让m+n总是为偶数

    通过虚拟加入‘#’,我们让m转换成2m+1 ,n转换成2n+1, 两数之和就变成了2m+2n+2,恒为偶数。

    注意是虚拟加,其实根本没这一步,通过下面的转换,我们可以保证虚拟加后每个元素跟原来的元素一一对应

     

    这么虚拟加后,每个位置可以通过/2得到原来元素的位置:

    比如 2,原来在0位,现在是1位,1/2=0

    比如 3,原来在1位,现在是3位,3/2=1

    比如 5,原来在2位,现在是5位,5/2=2

    比如 9,原来在3位,现在是7位,7/2=3

    而对于割(Cut),如果割在‘#’上等于割在2个元素之间,割在数字上等于把数字划到2个部分,总是有以下成立:

    LMaxi = (Ci-1)/2 位置上的元素
    RMini = Ci/2 位置上的元素

    例如:

    割在3上,C = 3,LMax=a[(3-1)/2]=A[1],RMin=a[3/2] =A[1],刚好都是3的位置!

    割在4/7之间‘#’,C = 4,LMax=A[(4-1)/2]=A[1]=4 ,RMin=A[4/2]=A[2]=7

    剩下的事情就好办了,把2个数组看做一个虚拟的数组A,A有2m+2n+2个元素,割在m+n+1处,所以我们只需找到m+n+1位置的元素和m+n+2位置的元素就行了。

    左边:A[m+n+1] = Max(LMax1,LMax2)

    右边:A[m+n+2] = Min(RMin1,RMin2)

    ==>Mid = (A[m+n+1]+A[m+n+2])/2 = (Max(LMax1,LMax2) + Min(RMin1,RMin2) )/2

    最快的割(Cut)是使用二分法,

    有2个数组,我们对哪个做二分呢?
    根据之前的分析,我们知道了,只要C1或C2确定,另外一个也就确定了。这里,为了效率,我们肯定是选长度较短的做二分,假设为C1。

    LMax1>RMin2,把C1减小,C2增大。—> C1向左二分

    LMax2>RMin1,把C1增大,C2减小。—> C1向右二分

    如果C1或C2已经到头了怎么办?

    这种情况出现在:如果有个数组完全小于或大于中值。假定n<m, 可能有4种情况:

    C1 = 0 —— 数组1整体都在右边了,所以都比中值大,中值在数组2中,简单的说就是数组1割后的左边是空了,所以我们可以假定LMax1 = INT_MIN

    C1 =2n —— 数组1整体都在左边了,所以都比中值小,中值在数组2中 ,简单的说就是数组1割后的右边是空了,所以我们可以假定RMin1= INT_MAX,来保证LMax2<RMin1恒成立

    C2 = 0—— 数组2整体在右边了,所以都比中值大,中值在数组1中 ,简单的说就是数组2割后的左边是空了,所以我们可以假定LMax2 = INT_MIN

    C2 = 2m—— 数组2整体在左边了,所以都比中值小,中值在数组1中, 简单的说就是数组2割后的右边是空了,为了让LMax1 < RMin2恒成立,我们可以假定RMin2 = INT_MA

    题目解答

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 class Solution {
     5 public:
     6     double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
     7         int n = nums1.size();
     8         int m = nums2.size();
     9         if (n > m){
    10             return findMedianSortedArrays(nums2, nums1);
    11         }
    12         int LMax1, LMax2, RMin1, RMin2, c1, c2, lo = 0, hi = 2 * n;
    13         while (lo <= hi){
    14             c1 = (lo + hi) / 2;  //c1是二分的结果
    15             c2 = m + n - c1;
    16             LMax1 = (c1 == 0) ? INT_MIN : nums1[(c1 - 1) / 2];
    17             RMin1 = (c1 == 2 * n) ? INT_MAX : nums1[c1 / 2];
    18             LMax2 = (c2 == 0) ? INT_MIN : nums2[(c2 - 1) / 2];
    19             RMin2 = (c2 == 2 * m) ? INT_MAX : nums2[c2 / 2];
    20             cout << LMax1<<" "<<RMin1<<endl;
    21             cout << LMax2<<" "<<RMin2<<endl;
    22             if (LMax1 > RMin2)
    23                 hi = c1 - 1;
    24             else if (LMax2 > RMin1)
    25                 lo = c1 + 1
    26 ;            else
    27                 break;
    28         }
    29         return (max(LMax1, LMax2) + min(RMin1, RMin2)) / 2.0;
    30     }
    31 };
    32 
    33 int main(){
    34     vector<int> a = {1,2,3};
    35     vector<int> b = {4,5,6,7};
    36     Solution su;
    37     cout << su.findMedianSortedArrays(a,b) << endl;
    38     return 0;
    39 }

    题解转自https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/4-xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-shu/

  • 相关阅读:
    android的FATAL EXCEPTION: main错误
    ORA12560: TNS: 协议适配器错误
    BroadcastReceiver应用详解
    using C# 详解
    mysqlproxy0.8版本读写分离测试总结
    Mysql SHOW PROCESSLIST Sending data
    [转载]大型网站运维探讨和心得
    找出并optimization表
    mysql中普通索引和唯一索引的效率对比
    ps aux 中STAT 解释
  • 原文地址:https://www.cnblogs.com/zllwxm123/p/11440917.html
Copyright © 2020-2023  润新知