4. Median of Two Sorted Arrays(topK-logk)

4. Median of Two Sorted Arrays

题目

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)).

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

解析

  • 题目是这样的:给定两个已经排序好的数组(可能为空),找到两者所有元素中第k大的元素。另外一种更加具体的形式是,找到所有元素的中位数。本篇文章我们只讨论更加一般性的问题:如何找到两个数组中第k大的元素?不过,测试是用的两个数组的中位数的题目,Leetcode第4题 Median of Two Sorted Arrays

  • 方案1:假设两个数组总共有n个元素,那么显然我们有用O(n)时间和O(n)空间的方法:用merge sort的思路排序,排序好的数组取出下标为k-1的元素就是我们需要的答案。
    这个方法比较容易想到,但是有没有更好的方法呢?

  • 方案2:我们可以发现,现在我们是不需要“排序”这么复杂的操作的,因为我们仅仅需要第k大的元素。我们可以用一个计数器,记录当前已经找到第m大的元素了。同时我们使用两个指针pA和pB,分别指向A和B数组的第一个元素。使用类似于merge sort的原理,如果数组A当前元素小,那么pA++,同时m++。如果数组B当前元素小,那么pB++,同时m++。最终当m等于k的时候,就得到了我们的答案——O(k)时间,O(1)空间。

  • 但是,当k很接近于n的时候,这个方法还是很费时间的。当然,我们可以判断一下,如果k比n/2大的话,我们可以从最大的元素开始找。但是如果我们要找所有元素的中位数呢?时间还是O(n/2)=O(n)的。有没有更好的方案呢?

 求中位数,给了两个例子。总结来看就是总数是偶数还是奇数。
奇数:(m+n)/2求得中间数,它是第(m+n)/2+1个数。这个值在我们利用归并思想解题时使用。
偶数:(m+n)/2、(m+n)/2+1。这两个数也就是第(m+n)/2、(m+n)/2+1个数。
利用归并思想查找到第k个数,按照运算规则即可。
 double findK(vector<int>&a,int lena,vector<int>&b,int lenb,int k)
    {
        int i=0,j=0;
        for(;i<lena&&j<lenb;)
        {
            k--;
            if(a[i]<b[j])
            {
                if(k==0)
                return a[i];
                i++;
            }
            else if(k==0)
            return b[j];
            else
            j++;
        }
        return i>=lena?b[j+k-1]:a[i+k-1];
    }
    double findMedianSortedArrays(vector<int>&nums1, vector<int>&nums2) {
        int m=nums1.size(),n=nums2.size();
        return ((m+n)&1)?findK(nums1,m,nums2,n,(m+n+1)>>1):
        ((findK(nums1,m,nums2,n,(m+n)>>1)+findK(nums1,m,nums2,n,((m+n)>>1)+1))*0.5);
    }
  • 我们可以考虑从k入手。如果我们每次都能够剔除一个一定在第k大元素之前的元素,那么我们需要进行k次。但是如果每次我们都剔除一半呢?所以用这种类似于二分的思想
题目中需要求出的结果是中位数,中位数的特点是其以后的数都比它大,前面的数都比它小。又因为两个数组都已经是有序数组,因为我们所需要的结果就是数组a中的第i个元素和数组b中第j个元素,使得i+j-2等于两个数组长度和的一半,所以此题就可以转换成求i,j这两个值的问题了。在数组a中确定i以及在数组b中确定j,此时可以采用二分查找的方法,通过不断缩小查找范围来确实所需要查找的值,也符合题目中所要求的分治算法的思想。

class Solution {  
public:  
    int getkth(int s[], int m, int l[], int n, int k){  
        //确保m < n   
        if (m > n)   
            return getkth(l, n, s, m, k);  
        if (m == 0)  
            return l[k - 1];  
        if (k == 1)  
            return min(s[0], l[0]);  
    //递归过程  
        int i = min(m, k / 2), j = min(n, k / 2);  
        if (s[i - 1] > l[j - 1])  
            return getkth(s, m, l + j, n - j, k - j);  
        else  
            return getkth(s + i, m - i, l, n, k - i);  
        return 0;  
    }  
      
    double findMedianSortedArrays(int A[], int m, int B[], int n) {  
        //总长度的一半  
        int l = (m + n + 1) / 2;  
        int r = (m + n + 2) / 2;  
        return (getkth(A, m ,B, n, l) + getkth(A, m, B, n, r)) / 2.0;  
    }  
};

题目来源

原文地址:https://www.cnblogs.com/ranjiewen/p/8283317.html