leetcode第一刷_Median of Two Sorted Arrays

这道题是我最初刷的那20多道之一,但一直没有过,被各种各样的情况折磨死了,直到把所有其他的题都写完,回来看大神对这道题是怎么处理的时候,才惊叹算法的奇妙。再次验证了我的想法,如果要处理各种各样的特殊情况,一定是算法本身有问题。

之前看过很多有关在两个排序数组中找中位数的解法,大多根据两个数组长度不同分了很多种情况,各种讨论。下面要介绍的方法并没有直接求中位数,而是把求中位数转换成了求两个数组合并之后的第(m+n)/2个数(当然根据总数的奇偶有所不同,不过思路上是没有问题的)。用k来表示进入递归的时候,应该求的两个数列合并之后的那个数,用二分的思想,我们先看A[k/2]与B[k/2]的大小关系,如果A[k/2-1]<B[k/2-1],那么A[0,k/2-1]中有没有可能存在合并之后的第K个数呢?A[k/2-1]大于A中在它之前的k/2-1个数,最好的情况,它也大于B中的在B[k/2-1]之前的k/2-1个数,也就是A[k/2-1]最大在合并后的数列中可以排到第(k/2-1+k/2-1+1)个位置,也即第k-1个位置。因此A[k/2-1]这一部分肯定不包含结果,应该直接扔掉。

对称的,当A[k/2-1]>B[k/2-1]时,有类似的结果。

当A[k/2-1] == B[k/2-1]时呢?说明这个数找到了,返回即可。

在实现的时候有几个技巧,两个数列个数不一样,为了下面编码部分的简便,可以在开头判断一下,如果前面数组个数多,就互换一下。第二点,数组中剩余的的元素可能比k/2要少,这时候就取一个较小的值即可。至于最开始两个数组合并后元素个数奇偶的问题,正是由于我们求的是第k个数而不是中位数,大大简化 了。设总数为t,如果是奇数,就求第t/2+1的那个数,如果是偶数,就分别求第t/2和第t/2+1的数,然后求平均。

28行代码完成了我100多行没有完成的事情,算法太美。

class Solution {
public:
    double findKth(int *A, int m, int *B, int n, int k){
        if(m>n){
            return findKth(B, n, A, m, k);
        }
        if(m == 0)
            return B[k-1];
        if(k == 1)
            return min(A[0], B[0]);
        int pa = min(m, k/2), pb = k-pa;
        if(A[pa-1]<B[pb-1]){
            return findKth(A+pa, m-pa, B, n, k-pa);
        }else if(A[pa-1]>B[pb-1]){
            return findKth(A, m, B+pb, n-pb, k-pb);
        }else{
            return A[pa-1];
        }
    }
    double findMedianSortedArrays(int A[], int m, int B[], int n) {
        int t = m+n;
        if(t&0x1){
            return findKth(A, m, B, n, t/2+1);
        }else{
            return (findKth(A, m, B, n, t/2)+findKth(A, m, B, n, t/2+1))/2;
        }
    }
};
点赞