LeetCode 4 寻找两个有序数组的中位数 题解

题目大意

给定两个有序数组,求这两个数组的中位数,时间复杂度要求是O(log(n+m))。

分析

这道题各种解法很多,我这里提供了关于中位数定义的那种解法。

简单来讲,中位数就是找到一组有序数中间位置的值,如果这组数有偶数个,那么中位数是最中间两个数的平均值,奇数个那么就是正中间的那个数。

把数组nums1和数组nums2分别在 i 和 j 进行划分,将 i 的左边和 j 的左边组合成“左半部分”,将 i 的右边和 j 的右边组合成“右半部分”。

1. 当 nums1数组和nums2数组的总长度是偶数时,如果能够保证

(1) 左半部分的长度等于右半部分,即:

i + j = m - i + n - j \Rightarrow j = ( m + n ) / 2 - i
(2) 左半部分最大值小于等于右半部分最小值

max(nums1[i - 1], nums2[j - 1]) \leq min(nums1[i], nums2[ j ])

那么,中位数就可以表示如下

(左半部分最大值 + 右半部分最小值 )/ 2。

(max(nums1[i - 1], nums2[j - 1]) + min (nums1[ i ] , nums2[ j ])) / 2
 

2. 当nums1数组和nums2数组的总长度是奇数时,如果能够保证

(1) 左半部分的长度比右半部分大1,即:

i + j = m - i + n - j + 1 \Rightarrow j = ( m + n + 1) / 2 - i
(2) 左半部分最大值小于等于右半部分最小值

max(nums1[i - 1], nums2[j - 1]) \leq min(nums1[ i ] , nums2[ j ])

那么,中位数就是左半部分最大值,也就是左半部比右半部分多出的那一个数:max(nums1[i - 1], nums2[j - 1])
上边的两种情况下的条件一其实可以合并为 j=(m+n+1)/2-i,因为如果m+n是偶数,由于取的是int值,所以加1也不会影响结果。另外在0<=i<=m时,为了保证0<=j<=n,我们必须保证 m <= n,原因可以通过不等式的简单放缩得到(这里可以理解为i是自变量,然后去控制函数 j 的值域):

最后一步由于是整数间的运算,所以 1/2=0。

而对于第二个条件,奇数和偶数的情况是一样的,进一步分析。

为了保证max(nums1[i - 1], nums2[j - 1]) \leq min(nums1[i], nums2[j])

因为数组是有序的,所以nums1[i - 1] <= nums1[i],nums2[i - 1] <= nums2[i]这是天然的,所以我们只需要保证nums2[j - 1] <= nums1[ i ] 和 nums1[ i - 1 ] <= nums2[ j ] 所以我们分两种情况讨论:

nums2[j - 1] > nums1[ i ],并且为了不越界,要保证 j != 0,i != m。
此时很明显,需要增加 i ,为了数量的平衡还要减少 j ,而由于 j = ( m + n + 1) / 2 - i,i 增大,j 自然会减少。

nums1[i - 1] > nums2[j],并且为了不越界,要保证 i != 0,j != n
此时和上边的情况相反,我们要减少 i ,增大 j 。

接下来讨论上述二种情况的边界:

1. 当 i = 0, 或者 j = 0,也就是切在了最前边。此时左半部分当 j = 0 时,最大的值就是 nums1[ i - 1 ] ;当 i = 0 时 最大的值就是 nums2[ j - 1] 。右半部分最小值和之前一样。

2. 当 i = m 或者 j = n,也就是切在了最后边。此时左半部分最大值和之前一样。右半部分当 j = n 时,最小值就是 nums1[ i ] ;当 i = m 时,最小值就是nums2[ j ] 。

最后需要考虑枚举 i 的方式,二分搜索即可。

代码

double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
    int m = nums1.size();
    int n = nums2.size();
    if(m > n) {
        return findMedianSortedArrays(nums2, nums1);
    }
    int iMin = 0, iMax = m;
    while(iMin <= iMax) {
        int i = (iMin + iMax) >> 1;
        int j = ((m + n + 1) >> 1) - i;
        if(j != 0 && i != m && nums2[j - 1] > nums1[i]) {
            iMin = i + 1;
        } else if(i != 0 && j != n && 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 == 1) {
                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 (minRight + maxLeft) / 2.0;
        }
    }
    return 0.0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值