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

Problem

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)).
You may assume nums1 and nums2 cannot be both empty.

Example

num1 = [1, 3]
num2 = [2]
median = 2

#include <stdio.h>
int max(int a, int b)
{
    return a > b ? a:b;
}

int min(int a, int b)
{
    return a < b ? a:b;
}
double findMedianSortedArrays1(int* nums1, int nums1Size, int* nums2, int nums2Size) {
    int t= (nums1Size + nums2Size + 1) / 2;
    int i = 0, j = 0;
    int res = 0;

    while(t--) {
        if(i >= nums1Size) {
            res = nums2[j++];
        }
        else if(j >= nums2Size) {
            res = nums1[i++];
        }
        else if(nums1[i] < nums2[j]) {
            res = nums1[i++];
        }
        else {
            res = nums2[j++];
        }
    }
    //printf("%d\n", res);
    int tol = nums1Size + nums2Size;
    if(tol % 2 == 1)
        return res;
    int res2 = 0;
    if(i >= nums1Size) {
        res2 = nums2[j++];
    }
    else if(j >= nums2Size) {
        res2 = nums1[i++];
    }
    else if(nums1[i] < nums2[j]) {
        res2 = nums1[i];
    }
    else {
        res2 = nums2[j];
    }
    //printf("%d\n", res2);
    return (double)(res + res2)/2.0;
}

double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size){
    int *a, *b, n, m;
    if(nums1Size <= nums2Size) {
        a = nums1, b = nums2;
        m = nums1Size, n = nums2Size;
    }
    else {
        a = nums2, b = nums1;
        m = nums2Size, n = nums1Size;
    }

    int low = 0, high = m;
    int i, j;
    while(low <= high) {
        i = (low + high) / 2;
        j = (m + n + 1) / 2 - i;

        if(i > low && a[i - 1] > b[j]) {
            high = i - 1;
        }
        else if(i < high && b[j - 1] > a[i]) {
            low = i + 1;
        }
        else {
            int maxleft;
            if(i == 0) maxleft = b[j - 1];
            else if(j == 0) maxleft = a[i - 1];
            else maxleft = max(b[j - 1], a[i - 1]);
            if((m + n) % 2 == 1) return maxleft;

            int minright;
            if(i == m) minright = b[j];
            else if(j == n) minright = a[i];
            else minright = min(a[i], b[j]);
            return (maxleft + minright) / 2.0;
        }
    }
    return 0.0;
}
int main()
{
    int a[100], b[100];
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 0; i < n; i++)
        scanf("%d", &a[i]);
    for(int j = 0; j < m; j++)
        scanf("%d", &b[j]);
    double res = findMedianSortedArrays(a, n, b, m);
    printf("%.1f\n", res);
    return 0;
}

TIP

  • 什么是中位数
中位数(Median)又称中值,统计学中的专有名词
是按顺序排列的一组数据中居于中间位置的数
如果观察值有偶数个,通常取最中间的两个数值的平均数作为中位数
在统计中,中位数被用来:
将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素

解题思路

理解了中位数的定义,我们就十分接近答案

方法一

数组是有序的,则可以采用归并的思想:
假设两个数组的规模为(m+n),每次从两个数组中取出剩下元素中较小的一个,一直取到第(m+n+1)/2次。
若(m+n)为奇数,则第(m+n+1)/2次所取的数即为答案;
若(m+n)为偶数,则需要再取一个数,与第(m+n+1)/2次所取的数的平均值即为答案

时间复杂度分析
  • 时间复杂度:O(m+n)
  • 空间复杂度:O(1)
方法二

利用中位数的性质:中位数左右两边的元素个数相等。
A数组里m个元素,所以有m+1中切分方法(i = 0~m)

		  left_A             |        right_A
    A[0], A[1], ..., A[i-1]  |  A[i], A[i+1], ..., A[m-1]
len(left_num1) = i, len(right_num1) = m - i
注意:当i=0 时,left_A 为空集,而当i=m 时,right_A 为空集。

采用同样的方式,我们在任一位置将B划分成两个部分

	       left_B            |        right_B
    B[0], B[1], ..., B[j-1]  |  B[j], B[j+1], ..., B[n-1]

将left_A 和eft_B 放入一个集合,并将right_A 和right_B 放入另一个集合。 再把这两个新的集合分别命名为left_part 和 right_part:

          left_part          |        right_part
    A[0], A[1], ..., A[i-1]  |  A[i], A[i+1], ..., A[m-1]
    B[0], B[1], ..., B[j-1]  |  B[j], B[j+1], ..., B[n-1]

那么当

len(left_part) = len(right_part)
max(left_part) <= min(right_part)

A和B中所有的元素就被我们划分为长度相同的两个部分,且其中一部分中的元素总是大于另外一部分中的元素,接下来就可以套用公式
m e d i a n = m a x ( l e f t _ p a r t ) + m i n ( r i g h t _ p a r t ) 2 median = \frac{max(left\_part) + min(right\_part)}{2} median=2max(left_part)+min(right_part)
要确保这两个条件,我们只需要保证:

  • i + j = m - i + n - j(或: m - i + n - j + 1)
  • 如果n>=m 只需要使 i = ( 0 , m ) j = m + n + 1 2 − i i=(0,m) \\ j = \frac{m+n+1}{2}-i i=(0,m)j=2m+n+1i
  • B[j - 1] <= A[i] && A[i - 1] <= B[j]

现在我们有len(left_part)=len(right_part)。 而且我们只会遇到三种情况
1.

B[j−1]≤A[i] 且 A[i−1]≤B[j],搜索结束 
B[j−1]>A[i]:这意味着 A[i] 太小,我们必须调整i以使B[j−1]≤A[i]。
我们可以增大i吗?
      是的,因为当 i被增大的时候,j 就会被减小。
      因此B[j−1] 会减小,而 A[i] 会增大,那么B[j−1]≤A[i] 就可能被满足。
我们可以减小i吗?
      不行,因为当i被减小的时候,jj 就会被增大。
      因此B[j−1] 会增大,而A[i] 会减小,那么B[j−1]≤A[i] 就可能不满足。
所以我们必须增大 ii。也就是说,我们必须将搜索范围调整为[i+1,imax]。
因此,设imin=i+1,并转到步骤 2。
A[i−1]>B[j]:
这意味着A[i−1] 太大,我们必须减小 ii 以使 A[i−1]≤B[j]。
也就是说,我们必须将搜索范围调整为[imin,i−1]。
因此,设imax=i−1,并转到步骤 2。
时间复杂度分析
  • 时间复杂度:O(log(min(m,n))),
    首先,查找的区间是[0,m]。
    而该区间的长度在每次循环之后都会减少为原来的一半。
    所以,我们只需要执行log(m) 次循环。由于我们在每次循环中进行常量次数的操作,所以时间复杂度为 O(log(m))。
    由于m≤n,所以时间复杂度是 O(log(min(m,n)))。
  • 空间复杂度:O(1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值