Median of Two Sorted Arrays

There are two sorted arrays A and B 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)).

 

从两个有序的数组中找到中位数,然后。。。然后这个题目真心变态!

方法一:归并排序,时间复杂度直接O(m+n),可以改进,找到第(m+n)/2个数,直接输出答案

方法二:设数组长度为n,元素为a0,a1,a2,a3,.....,an-1,那么

如果 n 是奇数,那么数组的中位数为 a[(n-1)/2],即上中位数和下中位数是同一个数

如果 n 是偶数,那么数组的上中位数为 a[(n-1)/2],下中位数为 a[n/2]

注:该题所求中位数是 (上中位数+下中位数)/2

因此需要分别求出两数组合并后的上中位数和下中位数。求上中位数的步骤如下:

1. 先保证A数组长度小于B数组,便于后面逻辑处理

2. 当A数组长度为0时,可以直接通过B数组求出上中位数

    当A数组长度为1时,这时B分3种情况,同时假设B的上中位数为B[bmid]

    a. B长度为1,那么上中位数就是他们之间的最小值

    b. B长度为奇数,那么需要比较A中仅有的元素与B[bmid]、B[bmid-1]想比较,进而得出两数组的中位数

    c. B长度为偶数,同样需要比较A中仅有的元素与B[bmid]、B[bmid+1]想比较,进而得出两数组的中位数

  当A数组长度大于1时,这是B数组长度大于等于A数组长度,此时我们可以直接得出A数组的上中位数A[amid],B数组的上中位数B[bmid]

  如果A[amid] = B[bmid],那么两数组的上中位数就是A[amid]

  如果A[amid] < B[bmid],那么说明两数组上中位数在A[amid]后面,B[bmid]前面,故可以同时裁剪 A数组中[0...m/2-1]元素及B[n-m/2....n-1]

    接着在A[m/2....m-1]和B[0.....n-m/2-1]继续找上中位数

  如果A[amid] > B[bmid],那么说明两数组的中位数在A[amid]前面,B[bmid]后面,故同样裁剪A后面m/2个元素,B前面m/2个元素

    接着在A[0.....m-m/2-1]和B[m/2....n-1]中继续找上中位数

  注:在数组中有一半的元素不超过中位数,有一半的元素不小于中位数,故 同时去除k个比中位数小的数,k个比中位数大的数,那么得到的子数组的中位数和原数组的中位数是相同的。

求下中位数的方法与上类似,代码如下:

 1 class Solution {
 2 public:
 3     double findMedianSortedArrays(int A[], int m, int B[], int n) {
 4         return ( 1.0*findUpMedian(A,m,B,n) + findDownMedian(A,m,B,n) ) / 2; //求上中位数和下中位数取均值
 5     }
 6 
 7     double findUpMedian(int A[], int m, int B[], int n) {   //求上中位数
 8         if( m > n ) {   //保证m<=n
 9             swap( A, B );
10             swap( m, n );
11         }
12         int amid = (m-1)/2, bmid = (n-1)/2;     //直接定位两数组的上中位数
13         if( m == 0 ) return B[bmid];
14         if( m == 1 ) {
15             if( n == 1 ) return min(A[amid], B[bmid]);
16             else if( n & 1 ) {   //奇数
17                 if( A[amid] < B[bmid-1] ) return B[bmid-1];
18                 else if( A[amid] < B[bmid] ) return A[amid];
19                 else return B[bmid];
20             }
21             else {  //偶数
22                 if( A[amid] < B[bmid] ) return B[bmid];
23                 else if( A[amid] < B[bmid+1] ) return A[amid];
24                 else return B[bmid+1];
25             }
26         }
27         else {
28             if( A[amid] == B[bmid] ) return A[amid];
29             int ncuts = m/2;    //每次切割都是切最小数组长度的一半
30             if( A[amid] < B[bmid] ) return findUpMedian(A+ncuts, m-ncuts, B, n-ncuts);
31             else return findUpMedian(A, m-ncuts, B+ncuts, n-ncuts);
32         }
33     }
34 
35     double findDownMedian(int A[], int m, int B[], int n) { //求下中位数
36         if( m > n ) {
37             swap( A, B );
38             swap( m, n );
39         }
40         int amid = m/2, bmid = n/2; //直接定位下中位数
41         if( m == 0 ) return B[bmid];
42         if( m == 1 ) {
43             if( n == 1 ) return max(A[amid], B[bmid]);
44             else if( n & 1 ) {   //奇数
45                 if( A[amid] > B[bmid+1] ) return B[bmid+1];
46                 else if( A[amid] > B[bmid] ) return A[amid];
47                 else return B[bmid];
48             }
49             else {  //偶数
50                 if( A[amid] < B[bmid-1] ) return B[bmid-1];
51                 else if( A[amid] < B[bmid] ) return A[amid];
52                 else return B[bmid];
53             }
54         }
55         else {
56             if( A[amid] == B[bmid] ) return A[amid];
57             int ncuts = m/2;    //每次切割都是切最小数组长度的一半
58             if( A[amid] < B[bmid] ) return findDownMedian(A+ncuts, m-ncuts, B, n-ncuts);
59             else return findDownMedian(A, m-ncuts, B+ncuts, n-ncuts);
60         }
61     }
62 };

方法三:转化为在两个有序数组中求第k个数最小数,步骤如下:

1. 如果a或b为空,那么直接求出第k小

2. 假设元素"平均分配"的情况下,如果第k小的数在a数组,设下标为i = m/(m+n)*(k-1),如果第k小的数在b数组,那么有 i+1 + j+1 = k,则 j = k - i - 2;

即保证了 保证a[i]及其前面的元素个数+b[j]及其前面的元素个数=k

此时,如果a[i] == b[j] 那说明第k-1及k的元素都为a[i]

若a[i] < b[j],那第k个数在a[i]后面,b[j]前面,就可以裁剪掉a数组前i+1个元素,在a[i+1....m],b[0....j]中找

若a[i] > b[j],那第k个数在b[j]后面,a[i]前面,就可以裁减掉b数组前j+1个元素,在b[j+1....n],a[0...i]中找

当k = 1或a[i] = b[i]的时候收敛,代码如下:

 

 1 class Solution {
 2 public:
 3     double findMedianSortedArrays(int A[], int m, int B[], int n) {
 4         if( (m+n) & 1 ) //元素个数为奇数的情况
 5             return findKthSmallest(A, m, B, n, (m+n+1)/2);
 6         else    //元素个数为偶数的情况
 7             return 1.0*(findKthSmallest(A, m, B, n, (m+n)/2)+findKthSmallest(A, m, B, n, (m+n)/2+1))/2;
 8     }
 9     
10     int findKthSmallest(int* a, int m, int* b, int n, int k) {
11         if( m == 0 ) return b[k-1];
12         if( n == 0 ) return a[k-1];
13         if( k == 1 ) return min(a[0], b[0]);    //收敛的条件
14         int i = 1.0*m/(m+n) * (k-1);    //在A数组大致预测i
15         int j = k-i-2;  //保证a[i]及其前面的元素个数+b[j]及其前面的元素个数=k,即i+1 + j+1 = k
16         if( a[i] == b[j] ) return a[i]; //如果相等,说明第k-1及k的元素都为a[i]
17         if( a[i] < b[j] ) return findKthSmallest(a+i+1, m-i-1, b, j+1, k-i-1);  //说明在a[i]后面,b[j]前面,故可裁剪a中前i+1个数
18         else return findKthSmallest(a, i+1, b+j+1, n-j-1, k-j-1);   //说明在b[j]后面,a[i]前面,故可裁剪b中前j+1个数
19     }
20 };

 

posted on 2014-09-06 16:50  bug睡的略爽  阅读(137)  评论(0编辑  收藏  举报

导航