LeetCode题解及面试

【LeetCode】4. Median of Two Sorte

2019-12-12  本文已影响0人  LeetPub

题意

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

解答一(递归,时间复杂度O(logk))

\color{red}{关键点:一步步筛选出k/2个已经确定在k个最小数中的元素}\

首先理解题意两个关键点:有序数组和中位数

根据上面性质可知,只要找到第(m+n)/2+1小和第(m+n)/2小的数即可,该题演变成找两个有序数组第k小的数

如何寻找两个有序数组的第k小数?二分查找

先声明查找第k小数的函数

/* param:
 *      a   数组a起始地址
 *      m   数组a起始地址开始的长度
 *      b   数组b起始地址
 *      n   数组b起始地址开始的长度
 */
int findKth(int* a, int m, int* b, int n, int k);

如果i=k/2,则j=k-i=k/2, a_0..a_{i-1}b_0..b_{j-1}总共k个数,但这k个数不一定是最小的k个数

findKth(a+i, m-i, b, n, k-i);
findKth(a, m, b+j, n-j, k-j);

二分核心流程:

int i = min(m, k/2), j = k-i;
if (a[i-1] < b[j-1]) {
    return findKth(a+i, m-i, b, n, k-i);
} else if (a[i-1] > b[j-1]) {
    return findKth(a, m, b+j, n-j, k-j);
} else {
    return a[i-1];
}

现在考虑边界情况

if (k==1) return min(a[0], b[0]);`
if (m == 0) return b[k-1];
if (n == 0) return a[k-1];

完整代码:

class Solution {
public:
    int findKth(int* a, int m, int* b, int n, int k) {
        if (m > n) return findKth(b, n, a, m, k);
        if (m == 0) return b[k-1];
        if (k == 1) return min(a[0], b[0]);
        int i = min(m, k/2), j = k - i;
        if (a[i-1] < b[j-1]) {
            return findKth(a+i, m-i, b, n, k-i);
        } else if (a[i-1] > b[j-1]){
            return findKth(a, m, b+j, n-j, k-j);
        } else {
            return a[i-1];
        }
    }
    
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size(), n = nums2.size();
        if ((m+n)%2) {
            return findKth(nums1.data(), m, nums2.data(), n, (m+n)/2+1);
        } else {
            int left = findKth(nums1.data(), m, nums2.data(), n, (m+n)/2);
            int right = findKth(nums1.data(), m, nums2.data(), n, (m+n)/2+1);
            cout<<left<<" "<<right<<endl;
            return (left+right)/2.0;
        }
    }
};

解答二(非递归,时间复杂度O(log(min(m,n))))

\color{red}{关键点:对一个数组进行二分查找,另一个数组相应的位置也会被相应确定}\
对于数组a_0..a_{m-1},有{m+1}种分隔方式,如下图(a)所示;当aa_{left}部分长度为i时,a_{right}部分长度为{m-i},如下图(b)所示;

同样的对于数组b_0..b_{n-1},有{n+1}种分隔方式,

如何求数组ab的中位数?按照上述划分原则,只要把数组ab划分成leftright两部分,保证{|a_{left}|+|b_{left}| = |a_{right}|+|b_{right}| },同时{max({left}) \le min({right})},左边元素都小于等于右边元素,则中位数为\frac{max({left})+min({right})}{2}

但是数组分偶数和奇数两种情况:

综合上述两个条件,不论m+n是奇数还是偶数,对于数组ab中位数来说满足j=(m+n+1)/2-i,为了保证{ j \ge 0},则
\begin{aligned} \frac{m+n+1}{2}-i \ge 0 =>{m+n+1} \ge {2i} => {m+n+1} \ge {2i} \ge {2m} => n \ge m-1 => n \gt m \\ \end{aligned}

故中位数满足的条件是:

边界条件:
m+n不论奇数偶数时

m+n为偶数时,需要min(right)

当对数组a进行二分查找位置i时,相应的数组b位置j=(m+n+1)/2-i,此时数组a和数组b左右两部分相等,中位数
media=\left\{ \begin{aligned} \frac{max({left})+min({right})}{2} & & m+n偶数 \\ \frac{max({left})}{2} & & m+n奇数 \\ \end{aligned} \right.

完整代码:

class Solution {
public:    
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size(), n = nums2.size();
        if (m > n) return findMedianSortedArrays(nums2, nums1);
        int imin = 0, imax = m, i = 0, j = 0, media = 0;
        while (imin <= imax) {
            i = (imin+imax)/2;
            j = (m+n+1)/2 - i;
            if (j-1>=0 && j-1<n && i>=0 && i<m  && nums2[j-1] > nums1[i]) {
                imin = i + 1;
            } else if (i-1>=0 && i-1<m && j>=0 && j< n  && nums1[i-1] > nums2[j]) {
                imax = i - 1;
            } else {    
                if (i == 0) media = nums2[j-1];
                else if (j == 0) media = nums1[i-1];
                else media = max(nums1[i-1], nums2[j-1]);
                break;
            }
        }
        if ((m+n)&1) return media;
        if (i == m) media += nums2[j];
        else if (j == n) media += nums1[i];
        else media += min(nums1[i], nums2[j]);
        return media*0.5;
    }
};
上一篇 下一篇

猜你喜欢

热点阅读