leetcode 4 Median of Two Sorted Arrays

来源:互联网 发布:天天飞车礼包 软件 编辑:程序博客网 时间:2024/06/05 03:31

Median of Two Sorted Arrays
Total Accepted: 59220 Total Submissions: 345234

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

首先说明一下,两个数列的中位数(median),就是把两个数列合在一起后,从小到大排列,位于中间的数,如果总的长度是奇数,那么中位数就是一个数,否则中位数就是中间两个数的平均数。这个题O(m+n)的算法,很简单,例如:两个数列各一个指针,我们要的是总的第k小的数,那么我们就比较两个指针指向的数,指向较小的数的那个指针就往后移,指向下一个数,然后继续与另一指针指向的数比较,哪一个数小,哪一个指针就往后移。这个过程每移一次就找出一个剩下的数列中的最小数,例如第一次移动,就找出最小数,第k次移动就找出第k小的数。

但题目要求要O(log(m+n)), 自己苦想也不得其解,网上搜索了下,发现了别人的一种解法,有人说是O(log(m+n)),我自己感觉可能是对数时间,但证明不了。不过这个解法的思路还是挺有意思的,所以记录下来。我所看的原帖地址:http://blog.csdn.net/yutianzuijin/article/details/11499917

double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {    double median;    int total = nums1Size + nums2Size;    if(total % 2 == 0)    {        median = (findKthElement(nums1, nums1Size, nums2, nums2Size, total/2) +         findKthElement(nums1, nums1Size, nums2, nums2Size, total/2+1)) / 2;     }    else    {        median = findKthElement(nums1, nums1Size, nums2, nums2Size, total/2+1);    }    return median;}

这个函数起始很简单,实现的功能就是之前描述的:如果总的长度是奇数,那么中位数就是第total2+1小的那个数,否则中位数就是中间两个数的平均数, 即:total2total2+1小的两个数的平均数。算法的关键还是findKthElement这个函数。

double findKthElement(int* nums1, int nums1Size, int* nums2, int nums2Size, int k){    double KthElement;    if(nums1Size > nums2Size)        KthElement = findKthElement(nums2, nums2Size, nums1, nums1Size, k);     else if(nums1Size == 0)    {        KthElement = nums2[k-1];    }    else if(k == 1)    {        KthElement = nums1[0] < nums2[0] ? nums1[0] : nums2[0];    }

首先我们要判断数组nums1和nums2哪个长,我们默认是nums2长于等于nums1,所以如果nums1长于nums2,那么我们就用递归的方式把两个数组交换位置。之所以保证nums1短于nums2,是因为我们后面的又操作时针对短的数组进行的,如果不确定哪个短,每次就都需要进行判断,代码就变得冗长。另外,我们要处理两种特殊情况,第一种是有一个数组为空,这是可能的,因为我们后面的算法会进行“删除”操作,因此数组可能变为空。这种情况下,我们要求第k小的元素,那显然就是剩下这个非空数组的第k小的元素,所以就是nums2[k-1],因为nums1比nums2短,所以非空的一定是nums2。另外一种情况,我们要求的是最小的元素,由于两个数列都是按升序排好了的,所以只需要比较nums1[0]和nums2[0],选择较小的那个即可。

    else    {        int i1 = min(nums1Size, k/2);        if(nums1[i1-1] < nums2[k-i1-1])        {            KthElement = findKthElement(nums1+i1, nums1Size-i1, nums2, nums2Size, k-i1);        }         else if(nums1[i1-1] > nums2[k-i1-1])        {            KthElement = findKthElement(nums1, nums1Size, nums2+k-i1, nums2Size-(k-i1), i1);        }        else        {            KthElement = nums1[i1-1];        }    }    return KthElement;}

其他的情况,我们要求i1,i1表示我们要求nums1中的第i1小的元素和nums2中的第k-i1小的元素,这两个元素是一定存在的,当i1等于nums1Size时,因为要求第k小,所以两数组的总数必然大于等于k,所以nums2Size必然大于等于k-nums1Size,所以nums2中必然包含大于等于k-i1(即k-nums1Size)个元素。当nums1等于k2时,说明nums1Sizek2, 所以nums2Sizenums1Sizek2,所以nums中必然存在第k-i1(即k-k2)小的元素.
我们要取两者的较小值,因为我们是要在两个数组总共取k个值,所以默认是各取k2个,但是又可能nums1比较短,总长度都没有k2,所以这个时候我们就去它的总长nums1Size,剩下的数从nums2中取,前面我已经说明了剩下的数必然可以从nums2中取出来。
我们现在已经取到了nums1中第i1小的数和nums2中第k-i1小的数,分别对应是数组中的nums1[i1-1]和nums2[k-i1-1]。然后我们来比较这两个数的大小,如果nums1[i1-1]<nums2[k-i1-1], 那么nums2[k-i1-1]这个数在合并后的数组中至少是第k小的,因为nums1[i1-1]比它小,说明nums1中的这前i1个数都比nums2[k-i1-1]小,而本身在nums2中已经有k-i1-1个数比它小,因此合并后至少有k-1个数比它小,因此它至少是第k小。之所以说是“至少”是因为nums1[i1-1]比它小,而nums1[i1-1]之后的其他数是否还有比它小的并不知道。
因此我们可以“删除”或者说忽略掉nums1中的这前i1个数,然后问题转化为:求从nums1中的第i1+1个数开始的剩下的数列和nums2数列合并后的第k-i1小的数。而这个数就是nums1和nums2合并后的第k小的数。这一点可以证明如下:如果求出来的这个第k-i1小的数(假设为a)来自nums1,那么它必然大于等于nums1中的前i1个数,所以把被忽略掉的这i1个数重新添加回去后,它们也都排在a的前面,所以a就变成第k小的数。如果求出来的数来自nums2,分两种情况:1. a前面k-i1个数中没有nums1的元素,那么a就是nums2里面的第k-i1小的元素,因为我们已知nums1[i1-1]nums2[k-i1-1],所以把被忽略掉的这i1个数重新添加回去后,它们也都排在a的前面,所以a就变成第k小的数。2. a前面有nums1的元素(假设其中最小的为b),因为nums1[i1-1]ba,所以把被忽略掉的这i1个数重新添加回去后,它们也都排在a的前面,所以a就变成第k小的数。

如果nums1[i1-1]>nums2[k-i1-1], 那么方法与上面的相同,只是这次被忽略掉的是nums2中的前k-i1个元素,然后在nums2从k-i1+1开始的剩余的数组和nums1中求出第i1小的元素。

另外,如果nums1[i1-1]=nums2[k-i1-1],那么这个值就是我们要找的第k小的元素。以nums1[i1-1]为例,nums2的前k-i1个元素全部可以放到它前面。nums2从第k-i1+1开始的元素全部可以放到它后面,因此它就是第k小的元素。

这个算法的好处就是每次比较后都能忽略掉一部分,使问题得规模减小,但并不是每次都减半,所以是否能够做到O(log(m+n)),我不确定。

下面附上完整代码:

#include <iostream>#include <stdlib.h>using namespace std;#define min(x,y) x<y?x:ydouble findKthElement(int* nums1, int nums1Size, int* nums2, int nums2Size, int k){    double KthElement;    if(nums1Size > nums2Size)        KthElement = findKthElement(nums2, nums2Size, nums1, nums1Size, k);     else if(nums1Size == 0)    {        KthElement = nums2[k-1];    }    else if(k == 1)    {        KthElement = nums1[0] < nums2[0] ? nums1[0] : nums2[0];    }    else    {        int i1 = min(nums1Size, k/2);        if(nums1[i1-1] < nums2[k-i1-1])        {            KthElement = findKthElement(nums1+i1, nums1Size-i1, nums2, nums2Size, k-i1);        }         else if(nums1[i1-1] > nums2[k-i1-1])        {            KthElement = findKthElement(nums1, nums1Size, nums2+k-i1, nums2Size-(k-i1), i1);        }        else        {            KthElement = nums1[i1-1];        }    }    return KthElement;}double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {    double median;    int total = nums1Size + nums2Size;    if(total % 2 == 0)    {        median = (findKthElement(nums1, nums1Size, nums2, nums2Size, total/2) +         findKthElement(nums1, nums1Size, nums2, nums2Size, total/2+1)) / 2;     }    else    {        median = findKthElement(nums1, nums1Size, nums2, nums2Size, total/2+1);    }    return median;}int main(){    cout<<"Input the size of nums1:"<<endl;    int nums1Size;    cin>>nums1Size;    int *nums1 = (int*)malloc(sizeof(int) * nums1Size);    int temp;    for(int i = 0; i < nums1Size; i++)    {        cin>>temp;        nums1[i] = temp;    }    cout<<"Input the size of nums2:"<<endl;    int nums2Size;    cin>>nums2Size;    int *nums2 = (int*)malloc(sizeof(int) * nums1Size);    for(int i = 0; i < nums2Size; i++)    {        cin>>temp;        nums2[i] = temp;    }    cout<<findMedianSortedArrays(nums1, nums1Size, nums2, nums2Size)<<endl;    return 0;}
0 0
原创粉丝点击