LeetCode[One hard A Day] - Median of Two Sorted Arrays

Meidan of Two Sorted Array

算法1(并非最优解):可以将 median 转化为第k小的数of two sorted arrays。(此处k为泛指,下面k为两个array size和)

             如果array size为双数,median为 (第k/2小+第(k/2+1)小)/2。For example, array size = 4, median = (第2小+第3小)/2.

             如果array size为单数,median为 第(k/2+1)小。For example, array size = 5, median = 第3小。

    如何找到第k小的数:

             Recursion

         假设两个array的size都不小于k/2,所以nums1[k/2-1] and nums2[k/2-1]都存在。我们先比较nums1[k/2-1] and nums2[k/2-1] (-1 is the offset for the 0-based index), 我们会得到如下3种情况:

            1.nums1[k/2-1] < nums2[k/2-1]

                如果有如上不等式,我们可以肯定的是nums1[k/2-1] < 第k小的数。证明1: 在现有的条件下,我们让nums1[k/2-1]尽量的大。nums1[k/2-1]前面有k/2-1个数,nums2[k/2-1]相同,所以nums1[k/2-1] and nums2[k/2-1]前面共有2*(k/2-1) = k-2个数。然后已知nums1[k/2-1] < nums2[k/2-1],还要让nums1[k/2-1]尽量的大,nums2[k/2-1]也要尽量的大。所以nums2[k/2-1]最大就为第k小的数,nums1[k/2-1]最大就为第k-1小的数,nums1[k/2-1]还是小于第k小的数。证明2(反证法): 假设nums1[k/2-1] > 第k小的数,假设其为第k+1小的数。由于nums1[k/2-1] < nums2[k/2-1], nums2[k/2-1]至少是(k+2)最小的数。nums1[k/2-1]前面有k/2-1个数,nums2[k/2-1]相同,所以nums1[k/2-1] and nums2[k/2-1]前面共有2*(k/2-1) = k-2个数。我们可以得到nums1[k/2-1]前面最多只有k-2个数,这与nums1[k/2-1]是第k+1最小矛盾。

            2.nums2[k/2-1] < nums1[k/2-1]

                同上论证我们可得nums2[k/2-1] < 第k小的数

            3.nums1[k/2-1] == nums2[k/2-1]

                因为nums1[k/2-1] and nums2[k/2-1]前面共有2*(k/2-1) = k-2个数,nums1[k/2-1] and nums2[k/2-1] 就是k-1小和第k小的数。

         如何handle其中一个array size 不足k/2的情况:其实去除前面的假设(两个array的size都不小于k/2), generalize 所有的情况我们发现nums1[index1] and nums2[index2] 前面共有k-2个数是之前三条结论成立的最重要条件,所以我们只要保证index1+index2 = k就行了。

         我们可以用以上的3种情况完成recursion。


         Base Case (Note: i = nums1已经check完的个数; j = nums2已经check完的个数)

            从recursion我们可以看出i在不断的递增并且会比j先达到它的上线,还有就是k不断的被除以2最后变为1。

            1.nums1.size()=i, nums1已经全部check完,第k小数只会在nums2中出现: return nums2[j+k-1]

            2.k=1, return min(nums1[i], nums2[j])


        最后别忘了handle k的奇偶性对最后答案的影响。


现在讲的这种方法是O(log(k)),上code  (并非最优解)

class Solution {
public:
        
    double findKth(vector<int> &nums1, int i, vector<int> &nums2, int j, int k){
        //make sure nums1.size()<nums2.size()
        if(nums1.size()-i>nums2.size()-j)
            return findKth(nums2, j, nums1, i, k);
        
        //base case
        if(nums1.size()==i) return nums2[j+k-1]; //i,j is the offset here, so we can also see it as nums2[k-1]
        if(k==1) return min(nums1[i], nums2[j]); //min(nums1[i+1-1], nums2[j+1-1]);
        
        //recursion
        int pa = min(i+k/2, int(nums1.size())), pb = j+k-pa+i; //want k-pa, j is the offset, we need to add i back since pa include i
        if(nums1[pa-1]<nums2[pb-1])
            return findKth(nums1, pa, nums2, j, k-pa+i);
        else if(nums1[pa-1]>nums2[pb-1])
            return findKth(nums1, i, nums2, pb, k-pb+j);
        else return nums2[pb-1];
        
        
    }
    
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int total = nums1.size() + nums2.size();
        if(total%2==1)
            return findKth(nums1, 0, nums2, 0, total/2+1);
        else 
            return (findKth(nums1, 0, nums2, 0, total/2)+findKth(nums1, 0, nums2, 0, total/2+1))/2;
        
    }
};

attention: 

Line 14: no matching function for call to 'min(int, std::vector<int>::size_type)'

solution: int(nums1.size())


最优解:log(min(n,m))

        上面的算法分了两种情况来考虑,odd length and even length。但其实我们可以直接用binary search直到最后一步再用考虑奇偶性。为了方便理解,我们先考虑所有array length为偶数。在这一前提下,“median” 的定义:我们把一串数切成两段,然后median就是前半段数的最大值和后半段数的最小值的平均数。举例,[2 3 5 7] -> [2 3 / 5 7],平均数为3和5的平均数。定义前半段数的最大值为L,后半段数最大值为R。[2 3 4 5]中L=3 and R=5。

        以上都是讨论一条array的情况,那么对于两条array来说,其实就是用两个cut将两条array这个整体分成数量相等的两份,median为前半段最大数和后半段最小数的平均值(前半段所有数都小于后半段都有数)。因为前后两段的数量要相等,只要一个array在哪里切决定后,第二个array在哪里切也就确定了。在这个前提下,我们只需要用binary search遍历短的那条array就行了。

        跟上一解相同,我们用一个if来保证nums1.length<nums2.length。因为要用binary search,我们需要有两个pointer,这里为cutL and cutR。在一开始我们让 cutL = 0 and cutR = nums1.size(),while loop的condition为cutL<=cutR。cut1为nums1 cut的地方,相同cut2为nums2 cut的地方。因为我们要遍历nums1, cut1 = (cutR-cutL)/2+cutL。cut1定下来了,随之cut2 = len/2-cut1因为我们需要cut1+cut2=len/2。然后我们就需要得到L1, L2, R1 and R2。因为cut1 and cut2是1 based index, 所以我们需要-1。然后L1 = nums1[cut1-1] and L2 = nums2[cut2-1]。因为有-1,cut1 and cut2 不能小于0。有了L1 and L2, 我们也可以得出R1 = nums1[cut1] and R2 = nums2[cut2],并且cut1 and cut2最大为nums1.size() and nums2.size() respectively。

        cut1和cut2能保证两条array分成的前后两部分数量相同,但是并不满足前半段所有数都小于后半段的所有数。这就可以成为我们推进遍历的条件。我们需要保证L1<R1, L1<R2, L2<R1, L2<R2。因为这两条array是排序好的,我们可以得到L1<R1 and L2<R2,所以只需要保证L1<R2 and L2<R1。

                if L1>R2, 说明cut1需要往左(小)移 -> cutR = cut1-1

                if L2>R1, 说明cut2需要往左(小)移,相对的cut1需要往右(大)移 -> cutL = cut1+1

        当median的条件都满足了后,我们考虑总长度的奇偶性来决定最后的median

               偶数:前半段最大数和后半段最小数的平均数

               奇数:后半段最小数

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        if(nums1.size() > nums2.size()){
            return findMedianSortedArrays(nums2, nums1);
        }

        int len = nums1.size()+nums2.size();
        int cut1 = 0;
        int cut2 = 0; 
        int cutL = 0; 
        int cutR = nums1.size(); 
        while(cutL<=cutR){
            cut1 = (cutR-cutL)/2+cutL;
            cut2 = len/2-cut1;
            cout << cut1<<" "<<cut2<<endl;
            double L1 = (cut1 == 0) ? INT_MIN : nums1[cut1 - 1];
            double L2 = (cut2 == 0) ? INT_MIN : nums2[cut2 - 1];
            double R1 = (cut1 == nums1.size()) ? INT_MAX : nums1[cut1];
            double R2 = (cut2 == nums2.size()) ? INT_MAX : nums2[cut2];

            if(L1>R2){
                cutR = cut1 - 1;
            }
            else if(L2>R1){
                cutL = cut1 + 1;
            }else{
                if(len%2==0){
                    L1 = L1>L2 ? L1:L2;
                    R1 = R1<R2 ? R1:R2;
                    return (L1+R1)/2;
                }
                else{
                    R1 = R1<R2 ? R1:R2;
                    return R1;
                }
            }
            
        }
        return -1;
    }
};


Lisa 呕心沥血为您打造

有些地方可能有点啰嗦,但是还是详尽为主~~~

Reference: http://www.cnblogs.com/grandyang/p/4465932.html

       https://leetcode.com/problems/median-of-two-sorted-arrays/discuss/2471/very-concise-ologminmn-iterative-solution-with-detailed-explanation

            https://blog.csdn.net/chen_xinjia/article/details/69258706

猜你喜欢

转载自blog.csdn.net/real_lisa/article/details/79576190