[LeetCode#4][C]Median of Two Sorted Arrays

来源:互联网 发布:西蒙智力量表软件 编辑:程序博客网 时间:2024/04/30 08:52

作者:faaronzheng 转载请注明出处!

题目如下:

这里写图片描述

思路:
题目求的是两个有序数组的中位数。首先明确一下中位数的概念(摘自百度百科)

中位数(又称中值,英语:Median),统计学中的专有名词,代表一个样本、种群或概率分布中的一个数值,其可将数值集合划分为相等的上下两部分。对于有限的数集,可以通过把所有观察值高低排序后找出正中间的一个作为中位数。如果观察值有偶数个,通常取最中间的两个数值的平均数作为中位数。

其次,有序是这个问题的关键,数组是否有序直接决定了解题策略。

最后,这道题对于时间复杂度是有要求的O(log(m+n))。

首先想到的方法是将两个有序数组合并,然后返回中位数。但是这样的时间复杂度是max(O(m),O(n)),因为memcpy函数本身的实现也是通过循环。虽然这样的时间复杂度理论上不符合题目要求。但是任然能通过,并且结果还不错。

代码:

double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {    int i=0,j=0;             //分别记录nums1和nums2的当前下标    int total=nums1Size+nums2Size;    int* res=(int*)malloc(sizeof(int)*total);    while(i<nums1Size||j<nums2Size)    {        if(i==nums1Size)     //nums1已经结束,将剩余nums2全部赋给res        {            memcpy(res+i+j,nums2+j,sizeof(int)*(nums2Size-j));             break;        }        if(j==nums2Size)    //nums2已经结束,将剩余nums1全部赋给res        {            memcpy(res+i+j,nums1+i,sizeof(int)*(nums1Size-i));              break;        }        //将nums1和nums2中小的赋给res        if(nums1[i]<=nums2[j])                 res[i+j]=nums1[i++];        if(nums1[i]>nums2[j])            res[i+j]=nums2[j++];    }    return total%2==0?(float)(res[total/2-1]+res[total/2])/2:res[total/2];}

结果:

这里写图片描述

进阶:
虽然上面的结果AC了。但是实际上并不符合题目的的要求。那么怎么才能达到log级的时间复杂度呢?假设循环变量为i,结束条件为i>=n,如果i每循环一次就乘以2,当循环结束,我们就可以得到关于循环次数m的下列等式:
2^m>=n
因此m=log2n。所以时间复杂度为log(n)。
那么在我们这道题中如何利用这种特性呢?如果每一次循环我们可以使得循环变量i乘以2或者使得结束条件n除以2。那么我们是不是就能达到这个目的了呢。

如何能让循环变量i乘以2或者使得结束条件n除以2呢?我们首先就要观察已有信息寻找突破点。我们已知的有用信息只有有序数组,那么有序的数组会有什么特点么?让我来看一下~

两个有序数组的中位数实际上是这两个数组合并后的第(m+n)/2小的数或第(m+n)/2和(m+n)/2+1的平均值(总数分别对应奇数和偶数时)。了解到这一点后,我们就可以用求第k小数的方法解决这个问题。假设数组A和B的元素个数都大于k/2,我们比较A[k/2-1]和B[k/2-1]两个元素,这两个元素分别表示A的第k/2小的元素和B的第k/2小的元素。这两个元素比较共有三种情况:>、< 和=。如果A[k/2-1] < B[k/2-1],这表示A[0]到A[k/2-1]的元素都在A和B合并之后的前k小的元素中。因此我们可以将其抛弃。当A[k/2-1]>B[k/2-1]时有类似的规律。最后如果A[k/2-1]=B[k/2-1]那么我们就找到了第k小数,也就找到了两个有序数组的中位数。正是因为这个特点,我们每次都可以抛弃k/2个数据。因此时间复杂度才能达到log级。实际上算法能达到log级的时间复杂度大多有类似的原理~

代码:

double KthNumber(int a[], int m, int b[], int n, int k)  {      if (m>n)          return KthNumber(b,n,a,m,k);      if (m==0)          return b[k-1];      if (k==1)          return a[0]>b[0]?b[0]:a[0];           int part1=k/2>m?m:k/2,part2=k-part1;  //分别计算两个数组k/2的下标    //三种情况    if (a[part1-1]<b[part2-1])          return KthNumber(a+part1,m-part1,b,n,k-part1);      else if (a[part1-1]>b[part2-1])          return KthNumber(a,m,b+part2,n-part2,k-part2);      else          return a[part1-1];  }double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {       int total = nums1Size + nums2Size;         if (total %2==0)     //偶数            return (KthNumber(nums1,nums1Size,nums2,nums2Size,total/2)+            KthNumber(nums1,nums1Size,nums2,nums2Size,total/2+1))/2;          else                //奇数         return KthNumber(nums1,nums1Size,nums2,nums2Size,total/2+1);             }

结果:

这里写图片描述

我们发现二者的时间一致(应该是巧合,还与测试数据的大小有关。)。那么问题来了,明明时间复杂度是降低了的。为什么时间开销没有降低呢。这就要考虑到递归是一种低效的操作。每一次的函数调用都会有额外的时间和空间开销。

注:由于测试数据的变化,代码每次提交后的运行时间不是一致的。但是结论还是一样的。

0 0
原创粉丝点击