LeetCode4. Median of Two Sorted Arrays(寻找第k小数:分治O(log(n+m)))

来源:互联网 发布:papi酱变音软件 编辑:程序博客网 时间:2024/04/29 09:12

题目链接:https://leetcode.com/problems/median-of-two-sorted-arrays/

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

Example 1:

nums1 = [1, 3]nums2 = [2]The median is 2.0

Example 2:

nums1 = [1, 2]nums2 = [3, 4]The median is (2 + 3)/2 = 2.5

解题思路1:

求两个有序数组的中位数,首先O(n+m)的算法很好想,二路归并的思想作一个新的数组,对数组寻秩访问,代码如下,LeetCode也能AC


class Solution {public:    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {        vector<int>v;        int median = nums1.size() + nums2.size();        int i,j;        for(i = 0,j = 0;i < nums1.size()&&j<nums2.size();){ //二路归并            if(nums1[i] < nums2[j]){                v.push_back(nums1[i]);                ++i;            }            else{                v.push_back(nums2[j]);                ++j;            }        }        if(i == nums1.size()){            while(j < nums2.size()) {v.push_back(nums2[j]); ++ j;}        }        else if(j == nums2.size()){            while(i < nums1.size()) {v.push_back(nums1[i]); ++ i;}        }        if(median&1){            return v[(median-1)/2] + 0.0;        }        return (v[median/2]+v[median/2-1])/2.0;    }};


但是题目要求的O(log(n+m))的算法,只能分而治之解决。


解题思路2: 


当m+n为偶数时,median = 第(m+n)/2个值 和 第(m+n)/2+1个值 的平均值;

当m+n为奇数时,median = 第(m+n)/2 + 1个值;


所以,我们可以把问题抽象成:O(log(n+m))的时间内,从两个有序数组A[ ] 、B[ ] 找到合并后的第k小值


为了解决这个问题,首先,我们要证明一个最关键的命题. 或者说,为什么可以用k/2去分治

假定,我们取A[ ]中第k/2小值,即A[k/2 - 1]这个元素(另pa = k/2),对应取B[ ]的第pb小值(令pb = k - pa);

{

    换句话说,我们从A[ ]、B[ ]两数组中取k个比较小的数,但是,这k个数并不一定包含合并后的第k小值,而是通过舍弃一些逐渐逼近;

}

那么,A[pa-1]和B[pb-1]存在以下三种关系:



1_____________

A[pa-1] == B[pb-1] : 说明第k小值一定在这k个数中间,而且必定等于A[pa-1]且等于B[pb-1];


2__________

A[pa-1] <   B[pb-1] : 结论是A[0..pa-1]一定不是第k小值,他们一定都比第k小值要小;从A[pa]往后去找;

{

  证明:反证法 \

   A[0] A[1] ...  A[pa-1].....         (pa = k/2)

   B[0] B[1] ... ......................B[pb-1]....... (pb = k - pa = k - k/2)

   当A[pa-1] < B[pb-1],我们说A[0..pa-1]<= A[pa-1] < 第k小值

   不妨设A[pa-1]是第k+1小值,B[pb-1]是第k+2小值,那么:

   A[ ]数组中比A[pa-1]小至多有k/2-1个,不包括自己;

   B[ ]数组中比A[pa-1]小至多有k - k/2 - 1个,不包括B[pb-1]因为B[pb-1] > A[pa-1];

  那么,比A[pa - 1]小的至多有(k/2-1) + (k - k/2 - 1) = k - 2个,这与A[pa-1]是第k+1小值矛盾!前者充其量只能说明: A[pa-1]是第k-1小值,是这个意思吧?!!!!

  所以,说A[0..pa-1]一定不是第k小值,他们一定都比第k小值要小,把他们舍弃即可;

}


3________

A[pa-1] >   B[pb-1] : 同理,结论是B[0..pb-1]一定不是第k小值,他们一定都比第k小值要小;从B[pb]往后去找;


综上所述,用k/2去分而治之,递归解决,注意递归基(边界条件):

1.若A[ ] 或 B[ ]为空,即只有一个数组,那么返回B[k-1] 或 A[k-1];

2.若k == 1,即合并后的最小值,那么返回A[0]和B[0]中的较小者即为全局最小值;


为了减少代码以实现边界条件1,可以维护保持第一个数组元素数量少于第二个(m < n),方法是交换,这样只需判断m == 0?即可;   A[ ]中没有k/2那么多元素的话,取到末尾m;


参考代码:


double find(int a[],int m,int b[],int n,int k){//第k小值    if(m > n) return find(b,n,a,m,k); //保证第一个数组是短的    if(!m) return b[k-1];            //a[]为空,返回b[]中第k个位置    if(k == 1) return min(a[0],b[0]);  //第1小值,是a[0]和b[0]中的较小者    int pa = min(k/2,m),pb = k - pa;        // 用k/2去分解    if(a[pa-1] < b[pb-1]) return find(a+pa,m-pa,b,n,k-pa);   //a[0..pa-1]肯定都在第k个位置的左边    else if(a[pa-1] > b[pb-1]) return find(a,m,b+pb,n-pb,k-pb);//b[0..pb-1]肯定也在第k个位置的左边    else return a[pa-1]; //第k个位置的值 == a[pa-1] == b[pb-1]}class Solution {public:    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {       int m = nums1.size();       int n = nums2.size();       int sum = m+n;       int *a = &nums1[0],*b = &nums2[0]; // vector => a[]       if(sum&1){           return find(a,m,b,n,(sum + 1)/2);   //问题抽象为查找两顺序数组的第k小值        }       else{           return (find(a,m,b,n,sum/2) + find(a,m,b,n,sum/2 + 1))/2.0;       }    }};




1 0
原创粉丝点击